LINUX.ORG.RU

Содержит ли данный код UB? Попытка №2

 


0

2
#include <iostream>
#include <memory>
#include <type_traits>
#include <cstddef>

//Public API

class Object;

class ObjectReferencePrivate;
class ObjectReference {
    friend class ObjectReferencePrivate;

private:
    ObjectReference() = default;
    void* operator new(std::size_t size);

public:
    Object & object() const;

    static void operator delete (void *p);
};

class Object {
protected:
    Object() = default;

public:
    void printHello() const;
    std::unique_ptr<ObjectReference> makeReference();
    virtual ~Object() = default;
};

std::unique_ptr<ObjectReference> makeObject();

// Implementation

static_assert(std::is_trivially_destructible<ObjectReference>::value, "ObjectReference must be trivially destructible");

class ObjectImpl: public Object {
public:
    ObjectImpl() = default;

    std::string mHelloString = "Hello!";
};

class ObjectReferencePrivate {
    friend class ObjectReference;

private:
    ObjectReferencePrivate() {
        new (&object) ObjectImpl;
    }

public:
    static std::unique_ptr<ObjectReference> makeReference()  {
        return std::unique_ptr<ObjectReference>(new ObjectReference);
    }

    ~ObjectReferencePrivate() {
        reinterpret_cast<ObjectImpl *>(&object)->~ObjectImpl();
    }

    int referenceCounter = 1;
    typename std::aligned_storage<sizeof(ObjectReference), alignof(ObjectReference)>::type q;
    typename std::aligned_storage<sizeof(ObjectImpl), alignof(ObjectImpl)>::type object;
};

static_assert(std::is_standard_layout<ObjectReferencePrivate>::value, "ObjectReferencePrivate must be standard layout");

Object & ObjectReference::object() const {
    const unsigned char * const_d_ptr = reinterpret_cast<const unsigned char *>(this) - offsetof(ObjectReferencePrivate, q);

    unsigned char * d_ptr = const_cast<unsigned char *>(const_d_ptr);

    ObjectReferencePrivate *d = reinterpret_cast<ObjectReferencePrivate *>(d_ptr);

    return *reinterpret_cast<ObjectImpl *>(&d->object);
}

void* ObjectReference::operator new(std::size_t) {
    return &(new ObjectReferencePrivate)->q;
}

void ObjectReference::operator delete (void *p) {
    unsigned char * d_ptr = reinterpret_cast<unsigned char *>(p) - offsetof(ObjectReferencePrivate, q);

    ObjectReferencePrivate *d = reinterpret_cast<ObjectReferencePrivate *>(d_ptr);

    std::cout << "Attempt to delete ObjectReference. referenceCounter is " << d->referenceCounter << "." << std::endl;

    if (--d->referenceCounter)
        return;

    std::cout << "Actualy deleting ObjectReference and object." << std::endl;

    delete d;
}

void Object::printHello() const {
    const std::string & helloString = static_cast<const ObjectImpl *>(this)->mHelloString;

    std::cout << helloString << std::endl;
}

std::unique_ptr<ObjectReference> Object::makeReference() {
    unsigned char * d_ptr = reinterpret_cast<unsigned char *>(this) - offsetof(ObjectReferencePrivate, object);

    ObjectReferencePrivate *d = reinterpret_cast<ObjectReferencePrivate *>(d_ptr);

    ++d->referenceCounter;

    return std::unique_ptr<ObjectReference>(reinterpret_cast<ObjectReference *>(&d->q));
}

std::unique_ptr<ObjectReference> makeObject() {
    return ObjectReferencePrivate::makeReference();
}

//Public API usage

int main(int argc, char* argv[]) {
    std::unique_ptr<ObjectReference> ref1 = makeObject();

    {
        std::unique_ptr<ObjectReference> ref2 = ref1->object().makeReference();
    }

    ref1->object().printHello();

    return 0;
}

Ссылка на предыдущее обсуждение

Этот код иллюстрирует то чего хочется достичь. Судя по тому что написано в стандарте, объект умирает после того как заканчивается вызов деструктора, если у него не тривиальный деструктор, или если записать что-либо в его память в противном случае (6.6.3 (1.3), (1.4)). В память объекта я не пишу, так что объект остаётся жить и после вызова delete. Условие что в delete expression можно сунуть только результат new expression тоже выполнено.

Хотелось обойтись без ObjectReferencePrivate в публичном API, но стандарт говорит что если статический и динамический типы объекта в delete expression различаются и нет виртуального деструктора, то UB (8.5.2.5 (3)).

Из минусов — operator new и delete в публичном API.


объект умирает после того как заканчивается вызов деструктора

при входе в деструктор, т. е. после того, как начинается вызов деструктора

если у него не тривиальный деструктор

с этим тоже осторожней: после завершения деструктора остаётся неинициализированная память, чтение которой UB(даже если деструктор тривиальный, как, например, у int)

//код не читал, ибо влом :-)

anonymous
()
Ответ на: комментарий от anonymous

даже если деструктор тривиальный, как, например, у int

У int вообще нет ни конструктора, ни деструктора.

anonymous
()
Ответ на: комментарий от anonymous

Может напи*дил слегка, но суть от этого не меняется. Also s/int/struct { int i; }/

anonymous
()
Ответ на: комментарий от hateyoufeel

Самое «понятное» объяснение ТС пока дал здесь: Содержит ли данный код UB? (комментарий) С небольшим уточнением здесь: Содержит ли данный код UB? (комментарий)

Вроде как ему нужен простой интрузивный умный указатель. Но человек вместо того, чтобы погуглить готовые реализации пытается изобрести лисапед.

eao197 ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Вход в профессию же - не всегда легко

anonymous
()
Ответ на: комментарий от eao197

www.linux.org.ru/forum/development/13952360?cid=13952803 (комментарий):

но проблема в том, что она должна хранить его в куче (указатель записанный в Java long)

Вроде в предыдущей теме это главное было(указатель на указатель, причём второй в куче)

anonymous
()
Ответ на: комментарий от anonymous

boost intrusive_ptr по прежнему решает поставленную проблему, но автор решил что лучше страдать

anonymous
()
Ответ на: комментарий от anonymous

Так этот третий указатель из java надо будет использовать, я, по крайней мере, так понял. Как boost::intrusive_ptr здесь поможет?

anonymous
()
Ответ на: комментарий от anonymous

Из C++ работаешь c intrusive_ptr<MyObject>, указатель сам дергает add_ref, release где надо.

Из Java работаешь напрямую с MyObject* завернутом в long, при необходимости дергая add_ref, release где надо руками.

anonymous
()
Ответ на: комментарий от anonymous

Из Java работаешь напрямую с MyObject* завернутом в long

Вопрос был именно в том, как избежать размещения указателя(long) в куче. В java все объекты в heap-е лежат?

anonymous
()
Ответ на: комментарий от anonymous

Какая тебе разница где оно в джаве лежит?

У тебя есть джава класс и цпп класс, первый содержит поле которое ссылается на объект второго.

anonymous
()
Ответ на: комментарий от hateyoufeel

Это уже just for fun. В предыдущей теме написал что остановился на std::shared_ptr.

com
() автор топика
Ответ на: комментарий от anonymous

Да мне-то никакой разницы, а вот ТС, вероятно, за идеалами гонится. А разницы между std::shared_ptr и boost::intrusive_ptr в этом ключе нет, посмотри предыдущую тему

anonymous
()

Я бы не заморачивался с микроинтерфейсами: ты делаешь некоторую задачу посредством кода на c++(по тем или иным причинам) - предоставляй для java интерфейс запуска/возврата результата для этой задачи(целой задачи, например, обсчитать нечто) вместо решений вида обеспечить эффективный и удобный способ использования крестовых smart pointer-ов из кода на java

anonymous
()
Ответ на: комментарий от com

И не нужно(хотя для boost::intrusive_ptr кода мало - хоть руками копируй). Разницы между intrusive и nonintrusive стратегиями здесь нет

anonymous
()
Ответ на: комментарий от anonymous

Там всё на самом деле интереснее: есть легаси в виде набора closed source нативных плагинов. Я открываю каждый из плагинов через dlopen, плагины открывают меня (тоже через dlopen) и предоставляют информацию которая должна быть в последствии доступна из Java.

Так что я бы не сказал что это прям микроинтерфейс получается. С ним, по сути, работаю и я и плагины.

com
() автор топика
Ответ на: комментарий от com

Ну я идеальный случай описал, с legacy кодом, разумеется, не всё так просто. Решение с приемлемым кол-вом костылей было в прошлой теме, ну а если в удовольствие разобраться/сделать лучше... Тебе здесь виднее, в общем

anonymous
()
Ответ на: комментарий от com

Из условий задачи твой объект имеет встроенный подсчёт ссылок, так что наличие add_ref в публичном плюсовом API ничего не меняет

anonymous
()
Ответ на: комментарий от anonymous

А разницы между std::shared_ptr и boost::intrusive_ptr в этом ключе нет, посмотри предыдущую тему

Как это нет? shared_ptr в java long ты не запихаешь никак. а intrusive_ptr можно преобразовать в голый указатель и при необходимости преобразовать обратно, не теряя владения объектом

anonymous
()

Тебе что, за длину строки кода платят? Пока дочитаешь до \r\n, забудешь как все начиналось...

anonymous
()

Содержит ли данный код UB?

cpp

over 100 строк

100% да

t184256 ★★★★★
()
Ответ на: комментарий от anonymous

ТС в прошлой теме использовал таки std::shared_ptr, УНВР

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.