Реализация и соблюдение общего поведения ABI на С++ в UNIX системах

Недавно начал писать свою библиотеку логирования для дальнейшего его использования в будущих проектах на языке С++. Сама библиотека уже готова, но вот проблема! Оказывается, при написании библиотек, требуется соблюдать общее поведение ABI. Я этого не знал! Начал читать про саму концепцию ABI, и в принципе, я понял, что от меня требуется. Но вот незадача! В C++ ABI описываются подробности на двоичном уровне, которые не специфицируются в Стандарте. Стандарт описывает общее поведение, но не говорит как оно должно быть реализовано. При попытке найти что то сносное, в котором будут показаны примеры соблюдения ABI, я ничего не нашел. Есть ли наглядные примеры, в которых подробно показывают, как правильно реализовать поведение ABI?

Вот примеры, которые я так понимаю, являются частью реализации ABI:


// Использование поддержки динамической видимости символов ELF C++

#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL  __attribute__ ((visibility ("hidden" )))

// ...

class DLL_LOCAL Sink
{
public:
 
    Sink(void) noexcept;
    Sink &operator=(const Sink &) noexcept = default;
    Sink &operator=(Sink &&)      noexcept = default;
    Sink(const Sink &)            noexcept = default;
    Sink(Sink &&)                 noexcept = default;
    virtual ~Sink(void)           noexcept = default;

    // ...
    
    virtual void print(const std::string &message) noexcept;
};

class DLL_PUBLIC StdoutSink : public Sink
{
public:

    StdoutSink(void) noexcept;
    StdoutSink &operator=(const StdoutSink &) noexcept = default;
    StdoutSink &operator=(StdoutSink &&)      noexcept = default;
    StdoutSink(const StdoutSink &)            noexcept = default;
    StdoutSink(StdoutSink &&)                 noexcept = default;
   ~StdoutSink(void) noexcept                 override = default;

    // ...

    void print(const std::string &message) noexcept override;
};

А так же, и такой пример:


class StdoutSinkImpl
{
public:

    StdoutSinkImpl(void) noexcept;
    StdoutSinkImpl &operator=(const StdoutSinkImpl &) noexcept = default;
    StdoutSinkImpl &operator=(StdoutSinkImpl &&)      noexcept = default;
    StdoutSinkImpl(const StdoutSinkImpl &)            noexcept = default;
    StdoutSinkImpl(StdoutSinkImpl &&)                 noexcept = default;
   ~StdoutSinkImpl(void)                              noexcept = default;

    // ...

    void print(const std::string &message) noexcept
    {
        std::cout << message << std::endl << std::flush;
    }
};

class StdoutSink
{
public:

    StdoutSink(void) noexcept : p_stdoutSinkImpl(new StdoutSinkImpl)
    {
        
    }
    
    StdoutSink &operator=(const StdoutSink &) noexcept = default;
    StdoutSink &operator=(StdoutSink &&)      noexcept = default;
    StdoutSink(const StdoutSink &)            noexcept = default;
    StdoutSink(StdoutSink &&)                 noexcept = default;
   ~StdoutSink(void) noexcept
    {
        if (!p_stdoutSinkImpl) {
            delete p_stdoutSinkImpl;
        }
    }

    // ...

    void print(const std::string &message) noexcept
    {
        p_stdoutSinkImpl->print(message);
    }

private:

    StdoutSinkImpl *p_stdoutSinkImpl = nullptr;
};

Во втором случае, мы скрываем класс реализации, от класса, которым будем пользоваться, для того, чтобы если при модификации полей класса StdoutSinkImpl, класс StdoutSink был бы неизменен, и не подтвергался перекомпиляции, так как приватный объект его всегда равен размеру указателя. Данный способ, я так полагаю, примерно реализует поведение ABI, но все же, не уверен. Но на счет первого примера, у меня нет идей, как правильно его использовать. Если у Вас есть идеи и конкретные примеры реализации ABI, я буду Вам очень благодарен.


Ответы (1 шт):

Автор решения: Tim

У Вас нет необходимости реализовывать ABI, особенно, если это не системная библиотека. Его реализовали за вас в компиляторах. Действительные и простые изменения в ABI Вы можете вносить меняя декларацию вызова функций, идущих на экспорт, но, опять же, бинарный интерфейс, в конечном счете, за компилятором.

Если интересно больше почитать про ABI в UNIX, то вот https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html

И в UNIX нет dll, есть so :)

→ Ссылка