2016-10-25 2 views
3

У меня есть приложение C++, которое построено с Xcode 7.2.1, скомпилировано с диалектом GNU ++ 98 и связано с libstdC++. Мне нужно сделать звонки в dylib, который скомпилирован с C++ 11 и связан с libC++.Может ли приложение скомпилировано против ссылки GNU ++ 98/libstdC++ для dylib, созданной против C++ 11/libC++?

Использования видимости атрибута по умолчанию в членах класса в dylib в:

extern __attribute__((visibility("default"))) void foo(std::string const&, int); 

... показывает, что не совместимость ABI (по крайней мере, для строк) - любая функция dylib, которая имеет параметры ЗПП: : строка не может быть связана с приложением.

Является подходящим решением для создания перегруженных функций-членов C++, которые передают только char * params, которые создаются на стороне dylib в std :: string, или есть другие gotchas, требующие создания чистого C API для dylib ?

Примечания: Теперь, после чтения This excellent explanation по вопросу совместимости ABI, он уверен, кажется, есть маленькая надежда, но и построил символ на основе API для станда :: строкового Params ...

+0

Создать класс типа строк? Или даже представление массива. С неявным преобразованием из std string и стандартного макета – Yakk

+0

Вы имеете в виду класс, который не был бы частью dylib? Или, если бы это было частью заголовков dylib, это был бы класс только для заголовков? – SMGreenfield

ответ

1

Написать свой API, используя стандартную раскладку классы.

Стандартные классы макета могут быть весьма причудливыми.

Вот Неоткомпилированная array_view с верхней части моей головы:

template<class Container> 
using data_ptr_type = decltype(std::declval<Container>().data()); 

template<bool b> 
using bool_kt = std::integral_constant<bool, b>; 

template<class T> 
struct array_view { 
    T* b = nullptr; 
    T* e = nullptr; 
    T* begin() const { return b; } 
    T* end() const { return b; } 
    template<class U> 
    using ptr_is_compatible = bool_kt< 
    std::is_same< U, T* >{} || std::is_same< U, std::remove_const_t<T>* >{} || 
    std::is_same< U, std::remove_volatile_t<T>* >{} || std::is_same< U, std::remove_cv_t<T>* >{} 
    >; 

    // convert from .data() and .size() containers: 
    template<class In, 
    std::enable_if_t< 
     ptr_is_compatible< data_ptr_type< In& > >{}, int 
    > = 0 
    > 
    array_view(In&& in):array_view(in.data(), in.size()) {} 

    // special ones: 
    array_view()=default; 
    array_view(array_view const&)=default; 
    array_view& operator=(array_view const&)=default; 

    // manual ones: 
    array_view(T* ptr, std::size_t N):array_view(ptr, ptr+N) {} 
    array_view(T* s, T* f):b(s), e(f) {} 

    // from C-style array: 
    template<class U, std::size_t N, 
    std::enable_if_t< 
     ptr_is_compatible< U* >{} 
    ,int> = 0 
    > 
    array_view(U(&arr)[N]):array_view(arr, N) {} 

    template<class Container> 
    Container copy_to() const { 
    return {begin(), end()}; 
    } 
}; 

Хотя фантазии, это стандартная раскладка. Таким образом, только самые безумные изменения ABI нарушат его.

Теперь ваш файл заголовка читает:

extern __attribute__((visibility("default"))) void foo(array_view<const char>, int); 

и вызывающий абонент может вызвать ее с foo("hello", 7) или foo(std::string("hello"), 42) или foo(std::vector<char>{'a', 'b', 'c'}, 18) или любой другой. Тебе все равно.

Указатели на начало буфера выполняются со стороны вызывающих абонентов, поэтому вы не знаете макет переданной вещи.

С внутренней стороны вы можете отправиться обратно в контейнер с arg.to<std::string>(), если вам это нужно.

Смежные вопросы