How to create a shared c library from a static c and c++ library?

Esm*_*ero 3 c c++ cmake shared-libraries static-libraries

Summarize

I'm integrating a static library based on c/c++ into a shared library based on c (using cmake on linux).

I have errors like this: libmylib.so: undefined reference to `std::ios_base::Init::Init()'

The problem is strongly related with the use of c++ and its linking to the shared library. If I avoid the shared library (even another static library) no error occurs. Because of my project I can not avoid that shared library which uses the static library.

How to generate a properly shared library from an c/c++ static library and new c source code?

PD: I'm sorry for the long question, I'm posting the code in order to give you some context of my problem.

C++ code

cppexample.hpp

typedef struct cpp_api
{
    int (*func_ptr)(const char *, int);
} cpp_api;

#ifdef __cplusplus
extern "C" {
#endif

const cpp_api* getApi();

#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)

cppexample.cpp

int apiFunc(const char *strc, int value)
{
  std::string str(strc);
  std::cout << "Api call: " << str << std::endl;
  return value;
}

static const cpp_api libapi =
{
    &apiFunc,
};

extern "C"
{
  const cpp_api* getApi()
  {
    return &libapi;
  }
}
Run Code Online (Sandbox Code Playgroud)

C code

example.h

void doSomething();
Run Code Online (Sandbox Code Playgroud)

example.c

#include "example.h"
#include "cpp_lib/cppexample.hpp"

void doSomething()
{
  const cpp_api *api = getApi();
  int result = api->func_ptr("hello!", 12);
}
Run Code Online (Sandbox Code Playgroud)

mainApi.h

void callDoSomething();
Run Code Online (Sandbox Code Playgroud)

mainApi.c

#include "mainApi.h"
#include "mix_lib/example.h"

void callDoSomething()
{
  doSomething();
}
Run Code Online (Sandbox Code Playgroud)

main.c

#include "mainApi.h"
int main(int argc, const char* argv[])
{
  callDoSomething();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Cmake code

Libary gene

cmake_minimum_required(VERSION 3.5)
set(CMAKE_BUILD_TYPE "Release") #Not debug
set(THIS MAINLIB) #project name
project(${THIS} C CXX) #For c and c++
set(CMAKE_C_STANDARD 99) #c99
set(CMAKE_CXX_STANDARD 11) #c++11
set(CMAKE_POSITION_INDEPENDENT_CODE ON) #fPIC

set(MAIN_LIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")

###### MAIN LIB ######
include_directories(${MAIN_LIB_ROOT} ${MAIN_LIB_ROOT}/main) 
list(APPEND MAIN_LIB_SOURCES
            "${MAIN_LIB_ROOT}/main/mainApi.h"
            "${MAIN_LIB_ROOT}/main/mainApi.c")
set(MAIN_LIB_NAME mainlib)
add_library(${MAIN_LIB_NAME} STATIC ${MAIN_LIB_SOURCES})
include("${MAIN_LIB_ROOT}/cpp_lib/cpp_lib.cmake")
include("${MAIN_LIB_ROOT}/mix_lib/mix_lib.cmake")
setup_cpp_lib()
setup_mix_lib()

###### MAIN EXECUTABLE ######
add_executable(${THIS} "${MAIN_LIB_ROOT}/main.c")
target_link_libraries(${THIS} PUBLIC ${MAIN_LIB_NAME})
list(APPEND MAIN_INSTALL_BINS 
            ${THIS})
Run Code Online (Sandbox Code Playgroud)

setup_cpp_lib()

list(APPEND CPP_LIB_SOURCES
            "${MAIN_LIB_ROOT}/cpp_lib/cppexample.hpp"
            "${MAIN_LIB_ROOT}/cpp_lib/cppexample.cpp")

function(setup_cpp_lib)
  add_library(cpplib OBJECT ${CPP_LIB_SOURCES})
  target_sources(${MAIN_LIB_NAME} PRIVATE $<TARGET_OBJECTS:cpplib>)
endfunction()
Run Code Online (Sandbox Code Playgroud)

setup_mix_lib()

list(APPEND MIX_LIB_SOURCES
            "${MAIN_LIB_ROOT}/mix_lib/example.h"
            "${MAIN_LIB_ROOT}/mix_lib/example.c")

function(setup_mix_lib)
  add_library(mixlib OBJECT ${MIX_LIB_SOURCES})
  target_sources(${MAIN_LIB_NAME} PRIVATE $<TARGET_OBJECTS:mixlib>)
endfunction()
Run Code Online (Sandbox Code Playgroud)

Until here the libmainlib.a is created. I copied that library and the mainApi.h and main.c to another project and the error occurs

Cmake with the error

cmake_minimum_required(VERSION 3.5)
set(CMAKE_BUILD_TYPE "Release") #Not debug
set(THIS MYPROJECT) #project name
project(${THIS} C CXX) #For c only
set(CMAKE_C_STANDARD 99) #c99
set(CMAKE_CXX_STANDARD 11) #c++11
set(MAIN_LIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}") #current cmakelists path

include_directories(${MAIN_LIB_ROOT}/includes) #includes for library headers
add_library(mylib SHARED "${MAIN_LIB_ROOT}/main.c")
add_library(mainlib STATIC IMPORTED)
set_property(TARGET mainlib PROPERTY IMPORTED_LOCATION ${MAIN_LIB_ROOT}/lib/libmainlib.a)
target_link_libraries(mylib mainlib)
add_executable(${THIS} "${MAIN_LIB_ROOT}/main.c")
target_link_libraries(${THIS} mylib)

list(APPEND MAIN_INSTALL_BINS ${THIS})
install(TARGETS ${MAIN_INSTALL_BINS} DESTINATION "${CMAKE_INSTALL_PREFIX}")
Run Code Online (Sandbox Code Playgroud)

Errors

libmylib.so: undefined reference to `std::ios_base::Init::Init()'
libmylib.so: undefined reference to `std::string::_Rep::_M_destroy(std::allocator<char> const&)'
libmylib.so: undefined reference to `std::string::_Rep::_S_empty_rep_storage'
libmylib.so: undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)'
collect2: error: ld returned 1 exit status
make[2]: *** [MYPROJECT] Error 1
make[1]: *** [CMakeFiles/MYPROJECT.dir/all] Error 2
make: *** [all] Error 2
Run Code Online (Sandbox Code Playgroud)

===Edit===

I have a static library called libmainlib.a (simulates a complex library that was modified injecting c++ code). That library is used to generate a shared library called libmylib.so (simulates another complex library that I didn't modify but uses the static library).

Tsy*_*rev 5

您得到了未定义的引用,因为mylib链接为C对象。但是由于静态库libmainlib.a是C ++ 库,因此它需要C ++链接。有关此类未定义引用的起源的更多信息,请提出以下问题:对`std :: ios_base :: Init :: Init()'的未定义引用

这种不正确的链接是因为mainlib第二个代码中的库STATIC IMPORTED很重要(两个关键字都很重要),而CMake并不知道该库的实际语言。

您需要暗示CMake,该给定的库实际上是C ++,并且需要C ++链接:

set_property(TARGET mainlib PROPERTY IMPORTED_LINK_INTERFACE_LANGUAGES CXX)
Run Code Online (Sandbox Code Playgroud)

您的第一个代码正确运行,因为它的mainlib库(虽然是STATIC)不是IMPORTED一个,并且CMake知道其源代码。作为源cppexample.cpp肯定是C ++,CMake将mainlib库视为C ++。