Esmeralda Quintero
Esmeralda Quintero

Reputation: 113

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

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

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;
  }
}

C code

example.h

void doSomething();

example.c

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

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

mainApi.h

void callDoSomething();

mainApi.c

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

void callDoSomething()
{
  doSomething();
}

main.c

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

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})

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()

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()

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}")

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

===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).

Upvotes: 3

Views: 327

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 66061

You got that undefined references because mylib library is linked as C object. But since the static library libmainlib.a is a C++ one, it requires C++ linking. For more info about origin of such undefined references that question: undefined reference to `std::ios_base::Init::Init()'.

Such incorrect linking is because the library mainlib in your second code is STATIC IMPORTED (both keywords are important) and CMake isn't aware of the actual language of that library.

You need to hint CMake that given library is actually a C++ one and requires C++ linking:

set_property(TARGET mainlib PROPERTY IMPORTED_LINK_INTERFACE_LANGUAGES CXX)

Your first code works correctly, because its mainlib library, while it is STATIC, is not IMPORTED one and CMake knows its sources. As a source cppexample.cpp is definitely a C++ one, CMake treats mainlib library as C++.

Upvotes: 5

Related Questions