Re: Циклические ссылки
От: GhostCoders Россия  
Дата: 07.06.16 17:20
Оценка:
На данный момент генерируются враппер-классы, используемые по значению.

То есть для класса
class Printer
{
private:
    void * printer_handle;
public:
     Printer()
     {
         printer_handle = printer_create();
     }
     ~Printer()
     {
         printer_destroy(printer_handler);
     }
     void Show()
     {
         printer_show(printer_handle);
     }
};


использование будет в духе:

{
    Printer printer_instance;
    printer_instance.Show();
}


То есть доступ идет через точку.
И сам класс, по сути являющийся легким враппером, имеет семантику копирования.

Проблема возникает в случае если класс А использует класс Б и наоборот.
Обычно, в С++ используют forward declaration, реализацию выносят в отдельный .cpp файл (от .h заголовка),
и в заголовках используют указатель или ссылку.

Такие циклические связи между классами необходимо отразить и в моих врапперах.
Но! Forward declaration работает только для указателей или ссылок, а у меня значения!

Поэтому пришлось немного исхитриться.
Для простого аргумента используется:

    void SetB(const Circular::ClassB& value)
    {
        struct raw_pointer_holder { void* raw_pointer; };
        circular_classa_setb(mObject, reinterpret_cast<const raw_pointer_holder*>(&value)->raw_pointer);
    }

и естественно, используется forward declaration для ClassB.

А для возвращаемых значений вот что:
    Circular::ClassBFwdPtr GetB()
    {
        return Circular::ClassBFwdPtr(circular_classa_getb(mObject));
    }


где Circular::ClassBFwdPtr есть:
namespace Circular
{
    namespace beautiful_capi
    {
        template<typename WrappedObjType>
        class forward_pointer_holder
        {
            void* m_pointer;
            bool m_object_was_created;
        public:
            explicit forward_pointer_holder(void* pointer)
             : m_object_was_created(false), m_pointer(pointer)
            {
            }
            ~forward_pointer_holder()
            {
                if (m_object_was_created)
                {
                    reinterpret_cast<WrappedObjType*>(this)->~WrappedObjType();
                }
            }
            operator WrappedObjType()
            {
                return WrappedObjType(m_pointer);
            }
            WrappedObjType* operator->()
            {
                m_object_was_created = true;
                return new(this) WrappedObjType(m_pointer);
            }
            void* get_raw_pointer() const
            {
                return m_pointer;
            }
        };
    }
    
    class ClassA;
    typedef beautiful_capi::forward_pointer_holder<ClassA> ClassAFwdPtr;
    class ClassB;
    typedef beautiful_capi::forward_pointer_holder<ClassB> ClassBFwdPtr;
}


То есть для возращаемых значений их можно присвоить враппер классу (благодаря оператору приведения),
или сделать вызов на лету, с использованием ->
    Circular::ClassA a_object; ...
    a_object.GetB()->Show(); // вызов на лету
    Circular::ClassB b_object = a_object.GetB();
    b_object.Show(); // а тут используем точку (".") как обычно


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

А как вам такая практика:

class Printer
{
private:
    void * printer_handle;
public:
     Printer()
     {
         printer_handle = printer_create();
     }
     ~Printer()
     {
         printer_destroy(printer_handler);
     }
     Printer* operator->()
     {
         return this;
     }
     const Printer* operator->() const
     {
         return this;
     }
     void Show()
     {
         printer_show(printer_handle);
     }
};


То есть можно использовать стрелку, даже для объектов-значений врапперов:
{
    Printer printer_instance;
    printer_instance->Show();
}


Есть ли подводные камни?
Третий Рим должен пасть!
Отредактировано 07.06.2016 17:24 GhostCoders . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.