how to create a shared object with high maintainability? - c++

I try to create a shared object for my C++ project with the purpose: every time I want to bring a new class, I recompile shared object only.
below is test code:
test.h:
#if !defined(_TEST_)
#define _TEST_
#include <string>
#include <memory>
class Test {
private:
public:
Test() {
}
~Test() {
}
virtual void show(const std::string &) = 0;
};
#ifdef __cplusplus
extern "C" {
#endif
// std::shared_ptr<Test> getObject();
#ifdef __cplusplus
}
#endif
#endif // _TEST_
getObject.cpp: class TestSo derive from Test.
#include <memory>
#include "testso.h"
std::shared_ptr<Test> getObject()
{
return std::make_shared<TestSo>();
}
and the main.cpp:
#include "test.h"
#include <dlfcn.h>
#include <string>
#include <memory>
std::shared_ptr<Test> (*getTestSo)();
int main(int argc, char const *argv[])
{
//...
void *handle = dlopen("/usr/lib/libfunc.so", RTLD_NOW);
if(handle == NULL)
return 1;
getTestSo = (std::shared_ptr<Test> (*)())dlsym(handle, "getObject");
const char *dlmsg = dlerror();
if(dlmsg != NULL) {
printf("dlsym: %s\n", dlmsg);
dlclose(handle);
return 1;
}
std::shared_ptr<Test> classSo = getTestSo();
classSo->show("Hi");
dlclose(handle);
//...
return 0;
}
I compile code with the following command line:
arm-none-linux-gnueabi-g++ -std=c++11 -c -fPIC getObject.cpp
arm-none-linux-gnueabi-g++ -shared getObject.o -o libtest.so
arm-none-linux-gnueabi-g++ -std=c++11 -L./ main.cpp -o test -ldl -ltest
compiler doesn't give me any warning or error. it says that undefined symbol: getObject when I run program.
finally I find that I must declare getObject in scope extern "C" within test.h.
dlsym fetch address of getObject from libtest.so and the program would jump to that address. why I must declare getObject? that is against my purpose. Or if I am totally going a wrong way, how do I do? if not, how can I fix it?
Edit:
arm-none-linux-gnueabi-nm -D libfunc.so
U __aeabi_atexit
U __aeabi_unwind_cpp_pr0
U __aeabi_unwind_cpp_pr1
0000f34c B __bss_end__
0000f34c B _bss_end__
0000f344 B __bss_start
0000f344 B __bss_start__
U __cxa_begin_catch
U __cxa_end_catch
U __cxa_end_cleanup
w __cxa_finalize
U __cxa_pure_virtual
U __cxa_rethrow
0000f344 D _edata
0000f34c B _end
0000f34c B __end__
00006664 T _fini
w __gmon_start__
U __gxx_personality_v0
00003e18 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
w __pthread_key_create
0000448c T _Z9getObjectv
U _ZdlPv
000045a4 W _ZdlPvS_
00004630 W _ZN4TestC1Ev
00004630 W _ZN4TestC2Ev
00004678 W _ZN4TestD1Ev
00004678 W _ZN4TestD2Ev
00004760 W _ZN6TestDl4showERKSs
000046c0 W _ZN6TestDlC1Ev
000046c0 W _ZN6TestDlC2Ev
00004710 W _ZN6TestDlD1Ev
00004710 W _ZN6TestDlD2Ev
0000569c W _ZN9__gnu_cxx13new_allocatorI6TestDlE7destroyIS1_EEvPT_
00005424 W _ZN9__gnu_cxx13new_allocatorI6TestDlE9constructIS1_IEEEvPT_DpOT0_
00005424 W _ZN9__gnu_cxx13new_allocatorI6TestDlE9constructIS1_JEEEvPT_DpOT0_
000052d8 W _ZN9__gnu_cxx13new_allocatorI6TestDlEC1ERKS2_
00004af8 W _ZN9__gnu_cxx13new_allocatorI6TestDlEC1Ev
000052d8 W _ZN9__gnu_cxx13new_allocatorI6TestDlEC2ERKS2_
00004af8 W _ZN9__gnu_cxx13new_allocatorI6TestDlEC2Ev
00004b1c W _ZN9__gnu_cxx13new_allocatorI6TestDlED1Ev
00004b1c W _ZN9__gnu_cxx13new_allocatorI6TestDlED2Ev
000050bc W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEE10deallocateEPS5_j
000056c0 W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEE7destroyIS5_EEvPT_
00005018 W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEE8allocateEjPKv
0000510c W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEE9constructIS5_IKS3_EEEvPT_DpOT0_
0000510c W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEE9constructIS5_JKS3_EEEvPT_DpOT0_
00004fd0 W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEEC1Ev
00004fd0 W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEEC2Ev
00004ff4 W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEED1Ev
00004ff4 W _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEED2Ev
000045c4 W _ZN9__gnu_cxx7__mutexC1Ev
000045c4 W _ZN9__gnu_cxx7__mutexC2Ev
000050e4 W _ZNK9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI6TestDlSaIS2_ELNS_12_Lock_policyE1EEE8max_sizeEv
00004e5c W _ZNKSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EE14_M_get_deleterERKSt9type_info
U _ZNKSt9type_infoeqERKS_
000051a0 W _ZNSaI6TestDlEC1ERKS0_
00004934 W _ZNSaI6TestDlEC1Ev
000051a0 W _ZNSaI6TestDlEC2ERKS0_
00004934 W _ZNSaI6TestDlEC2Ev
0000495c W _ZNSaI6TestDlED1Ev
0000495c W _ZNSaI6TestDlED2Ev
00004edc W _ZNSaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEEC1IS0_EERKSaIT_E
00004edc W _ZNSaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEEC2IS0_EERKSaIT_E
00004f08 W _ZNSaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEED1Ev
00004f08 W _ZNSaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEED2Ev
U _ZNSolsEPFRSoS_E
000048f4 W _ZNSt10shared_ptrI4TestEC1I6TestDlvEEOS_IT_E
000048f4 W _ZNSt10shared_ptrI4TestEC2I6TestDlvEEOS_IT_E
00004b40 W _ZNSt10shared_ptrI6TestDlEC1ISaIS0_EIEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004b40 W _ZNSt10shared_ptrI6TestDlEC1ISaIS0_EJEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004b40 W _ZNSt10shared_ptrI6TestDlEC2ISaIS0_EIEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004b40 W _ZNSt10shared_ptrI6TestDlEC2ISaIS0_EJEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004808 W _ZNSt10shared_ptrI6TestDlED1Ev
00004808 W _ZNSt10shared_ptrI6TestDlED2Ev
00005300 W _ZNSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EEC1Ev
00005300 W _ZNSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EEC2Ev
00004a8c W _ZNSt12__shared_ptrI4TestLN9__gnu_cxx12_Lock_policyE1EEC1I6TestDlvEEOS_IT_LS2_1EE
00004a8c W _ZNSt12__shared_ptrI4TestLN9__gnu_cxx12_Lock_policyE1EEC2I6TestDlvEEOS_IT_LS2_1EE
00004c00 W _ZNSt12__shared_ptrI6TestDlLN9__gnu_cxx12_Lock_policyE1EEC1ISaIS0_EIEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004c00 W _ZNSt12__shared_ptrI6TestDlLN9__gnu_cxx12_Lock_policyE1EEC1ISaIS0_EJEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004c00 W _ZNSt12__shared_ptrI6TestDlLN9__gnu_cxx12_Lock_policyE1EEC2ISaIS0_EIEEESt19_Sp_make_shared_tagRKT_DpOT0_
00004c00 W _ZNSt12__shared_ptrI6TestDlLN9__gnu_cxx12_Lock_policyE1EEC2ISaIS0_EJEEESt19_Sp_make_shared_tagRKT_DpOT0_
000047d8 W _ZNSt12__shared_ptrI6TestDlLN9__gnu_cxx12_Lock_policyE1EED1Ev
000047d8 W _ZNSt12__shared_ptrI6TestDlLN9__gnu_cxx12_Lock_policyE1EED2Ev
00004bb8 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EE7_M_swapERS2_
00004a5c W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EEC1Ev
00004d84 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EEC1I6TestDlSaIS4_EIEEESt19_Sp_make_shared_tagPT_RKT0_DpOT1_
00004d84 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EEC1I6TestDlSaIS4_EJEEESt19_Sp_make_shared_tagPT_RKT0_DpOT1_
00004a5c W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EEC2Ev
00004d84 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EEC2I6TestDlSaIS4_EIEEESt19_Sp_make_shared_tagPT_RKT0_DpOT1_
00004d84 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EEC2I6TestDlSaIS4_EJEEESt19_Sp_make_shared_tagPT_RKT0_DpOT1_
00004890 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EED1Ev
00004890 W _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE1EED2Ev
0000564c W _ZNSt16allocator_traitsISaI6TestDlEE10_S_destroyIS0_EENSt9enable_ifIXsrNS2_16__destroy_helperIT_EE5valueEvE4typeERS1_PS6_
000053fc W _ZNSt16allocator_traitsISaI6TestDlEE12_S_constructIS0_IEEENSt9enable_ifIXsrNS2_18__construct_helperIT_IDpT0_EEE5valueEvE4typeERS1_PS6_DpOS7_
000053fc W _ZNSt16allocator_traitsISaI6TestDlEE12_S_constructIS0_JEEENSt9enable_ifIXsrNS2_18__construct_helperIT_JDpT0_EEE5valueEvE4typeERS1_PS6_DpOS7_
000055fc W _ZNSt16allocator_traitsISaI6TestDlEE7destroyIS0_EEvRS1_PT_
000053d4 W _ZNSt16allocator_traitsISaI6TestDlEE9constructIS0_IEEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS1_PT_DpOS4_
000053d4 W _ZNSt16allocator_traitsISaI6TestDlEE9constructIS0_JEEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS1_PT_DpOS4_
00004fa0 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE10deallocateERS6_PS5_j
00005674 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE10_S_destroyIS5_EENSt9enable_ifIXsrNS7_16__destroy_helperIT_EE5valueEvE4typeERS6_PSB_
00005080 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE12_S_constructIS5_IKS2_EEENSt9enable_ifIXsrNS7_18__construct_helperIT_IDpT0_EEE5valueEvE4typeERS6_PSC_DpOSD_
00005080 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE12_S_constructIS5_JKS2_EEENSt9enable_ifIXsrNS7_18__construct_helperIT_JDpT0_EEE5valueEvE4typeERS6_PSC_DpOSD_
00005624 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE7destroyIS5_EEvRS6_PT_
00004f30 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE8allocateERS6_j
00004f64 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE9constructIS5_IKS2_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS6_PT_DpOSA_
00004f64 W _ZNSt16allocator_traitsISaISt23_Sp_counted_ptr_inplaceI6TestDlSaIS1_ELN9__gnu_cxx12_Lock_policyE1EEEE9constructIS5_JKS2_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS6_PT_DpOSA_
00004b7c W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE10_M_destroyEv
000049b4 W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE10_M_releaseEv
0000532c W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EEC1Ev
0000532c W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EEC2Ev
00004d0c W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EED0Ev
00004cb4 W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EED1Ev
00004cb4 W _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EED2Ev
0000553c W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE10_M_destroyEv
00005508 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE10_M_disposeEv
0000559c W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE14_M_get_deleterERKSt9type_info
00005398 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE5_ImplC1ES1_
00005398 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE5_ImplC2ES1_
000051d0 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE5_ImplD1Ev
000051d0 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE5_ImplD2Ev
000051f8 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEC1IIEEES1_DpOT_
000051f8 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEC1IJEEES1_DpOT_
000051f8 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEC2IIEEES1_DpOT_
000051f8 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EEC2IJEEES1_DpOT_
000054d8 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EED0Ev
00005464 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EED1Ev
00005464 W _ZNSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EED2Ev
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _Znwj
0000457c W _ZnwjPv
00004834 W _ZSt11make_sharedI6TestDlIEESt10shared_ptrIT_EDpOT0_
00004834 W _ZSt11make_sharedI6TestDlJEESt10shared_ptrIT_EDpOT0_
00004984 W _ZSt15allocate_sharedI6TestDlSaIS0_EIEESt10shared_ptrIT_ERKT0_DpOT1_
00004984 W _ZSt15allocate_sharedI6TestDlSaIS0_EJEESt10shared_ptrIT_ERKT0_DpOT1_
U _ZSt17__throw_bad_allocv
00004ec0 W _ZSt32__enable_shared_from_this_helperILN9__gnu_cxx12_Lock_policyE1EEvRKSt14__shared_countIXT_EEz
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
00004d3c W _ZSt4moveIRKSaI6TestDlEEONSt16remove_referenceIT_E4typeEOS5_
000048d0 W _ZSt4moveIRSt10shared_ptrI6TestDlEEONSt16remove_referenceIT_E4typeEOS5_
00004d60 W _ZSt7forwardIKSaI6TestDlEEOT_RNSt16remove_referenceIS3_E4typeE
U _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000f098 V _ZTI4Test
0000f08c V _ZTI6TestDl
0000f0c4 V _ZTIN9__gnu_cxx7__mutexE
0000f0b8 V _ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE
0000f0a0 V _ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE
0000f084 V _ZTISt19_Sp_make_shared_tag
0000f078 V _ZTISt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE
000066f8 V _ZTS4Test
000066f0 V _ZTS6TestDl
00006764 V _ZTSN9__gnu_cxx7__mutexE
00006734 V _ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE
00006700 V _ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE
000066d8 V _ZTSSt19_Sp_make_shared_tag
0000668c V _ZTSSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE
0000f048 V _ZTV4Test
0000f038 V _ZTV6TestDl
U _ZTVN10__cxxabiv117__class_type_infoE
U _ZTVN10__cxxabiv120__si_class_type_infoE
U _ZTVN10__cxxabiv121__vmi_class_type_infoE
0000f058 V _ZTVSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE
0000f018 V _ZTVSt23_Sp_counted_ptr_inplaceI6TestDlSaIS0_ELN9__gnu_cxx12_Lock_policyE1EE

You approach does not work, because C++ mangles names during compilation, maybe more on this topic can be found over the net or for example in this thread. When you compile a function named std::shared_ptr<Test> getObject() it ends up as a symbol named _Z9getObjectv, so in you need to open this symbol dlsym(handle, "_Z9getObjectv");
every time I want to bring a new class, I recompile shared object only.
I don't understand why do you don't want to just link against your program. dlsym was created to allow linker-related programs to handle the loading, the functionality seems not needed in your program. In case of your program simply adding a forward declaration of the function (without any extern C, because the name is mangled by C++):
std::shared_ptr<Test> getObject();
to your test.h file and then compiling with:
-ltest
as you do now(!) will link against shared library. That's exactly the reason why shared libraries exists in the first place - so you can change the underlying code without changing the client code. Also in case of linking the linker will issue symbol resolution error if the symbol is not found, so you have another layer of protection and you don't have to worry about mangled names. Just change your main.cpp to:
#include "test.h"
#include <dlfcn.h>
#include <string>
#include <memory>
int main(int argc, char const *argv[])
{
std::shared_ptr<Test> getObject();
std::shared_ptr<Test> (*getTestSo)() = getObject;
std::shared_ptr<Test> classSo = getTestSo();
return 0;
}
and it should work (with proper LD_LIBRARY_PATH)...

Below is a working example for you.
There is no need for extern "C" linkage as you can find out and use the mangled C++ name just fine.
Also, factory functions should return std::unique_ptr to convey to the user that they now own the object. Unless the factory keeps a reference to the object, in which case std::shared_ptr would be a good choice.
Shared library header:
// shared.h
#pragma once
#include <memory>
#include <string>
#include <iosfwd>
namespace shared {
struct Test {
virtual ~Test() = 0;
virtual void print(std::ostream&) const = 0;
// Factory function.
static std::unique_ptr<Test> create(std::string const& type);
};
inline std::ostream& operator<<(std::ostream& s, Test const& t) {
t.print(s);
return s;
}
} // namespace shared
Shared library source:
// shared.cc
#include "shared.h"
#include <iostream>
namespace {
struct A : shared::Test {
void print(std::ostream& s) const override {
s << __PRETTY_FUNCTION__;
}
};
} // namespace
shared::Test::~Test() = default;
std::unique_ptr<shared::Test> shared::Test::create(std::string const& type) {
std::unique_ptr<Test> result;
if(type == "A")
result.reset(new A);
return result;
}
Build the shared library and find the mangled name of Test::create:
$ g++ -c -fPIC -W{all,extra,error} -std=c++11 shared.cc
$ g++ -o libshared.so -shared shared.o
$ nm --demangle --defined-only --dynamic libshared.so | grep Test::create
0000000000001894 T shared::Test::create(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
$ nm --defined-only --dynamic libshared.so | grep 0000000000001894
0000000000001894 T _ZN6shared4Test6createERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
The user source:
// test.cc
#include "shared.h"
#include <iostream>
#include <dlfcn.h>
struct DlClose { void operator()(void* handle) const { ::dlclose(handle); } };
using dl_ptr = std::unique_ptr<void, DlClose>;
int main() {
dl_ptr handle(::dlopen("./libshared.so", RTLD_NOW));
if(!handle)
std::abort();
using FactoryFn = std::unique_ptr<shared::Test>(std::string const&);
FactoryFn* shared_create = reinterpret_cast<FactoryFn*>(::dlsym(handle.get(), "_ZN6shared4Test6createERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"));
if(!shared_create)
std::abort();
auto a = shared_create("A");
std::cout << *a << '\n';
}
Build and run the user code:
$ g++ -W{all,extra,error} -std=c++11 -o test -ldl test.cc
$ ./test
virtual void {anonymous}::A::print(std::ostream&) const

Related

dlopen() - Is this undefined symbol error related to dependencies?

The background of the issue is in an already answered question, here
I have a symbol that I want to use in a shared library but despite being listed as a T defined symbol via nm, F (function) via objdump, when I try to access it using dlopen() or via Python (ctypes.CDLL), it seemingly does not exist or is not defined.
I was reading here that this could be related to dependencies.
Output of ldd:
linux#host:~$ ldd ./compiled_program
linux-vdso.so.1 (0x00007ffde1fdf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3381e7c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f338246f000)
Output of dn -DC:
linux#host:~$ nm -DC ./compiled_program
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w __cxa_finalize
w __gmon_start__
U __libc_start_main
U __stack_chk_fail
U printf
U putchar
U puts
U rand
U srand
U time
With my limited knowlege of C, I am unable to tell if any of these might be unresolved dependencies and where to look for which libraries to load, as the answer suggests, to include them. Is it as simple as finding the library and adding an #include <...> or do I have to locate the library and load it with dlopen()? The answer seems to suggest the latter. I appreciate that I am completely flailing in the dark here!
My current dlopen code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
int main(int argc, char** argv) {
void *handle;
void (*func_in_question)(const char*);
handle = dlopen("./compiled_program", RTLD_LAZY);
if (!handle) {
/* fail to load the library */
fprintf(stderr, "Error: %s\n", dlerror());
return EXIT_FAILURE;
}
*(void**)(&func_in_question) = dlsym(handle, "function_I_would_like_to_use");
if (!func_in_question) {
/* no such symbol */
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(handle);
return EXIT_FAILURE;
}
func_in_question(argv[1]);
dlclose(handle);
return EXIT_SUCCESS;
}
comes back with the undefined symbol error.
I would be grateful for any pointers on, a) any other reasons why I might not be able to access this symbol?
and b) if my half-baked suspicion about dependencies is anywhere near to making sense!
EDIT: copying error message from original question linked above:
Error: ./compiled_program: undefined symbol: function_I_would_like_to_use
and the output of nm --defined-only ./compiled_program from original
question:
0000000000200d98 d _DYNAMIC
0000000000200f88 d _GLOBAL_OFFSET_TABLE_
0000000000000ba0 R _IO_stdin_used
0000000000000d64 r __FRAME_END__
0000000000000bfc r __GNU_EH_FRAME_HDR
0000000000201010 D __TMC_END__
0000000000201010 B __bss_start
0000000000201000 D __data_start
00000000000007d0 t __do_global_dtors_aux
0000000000200d90 t __do_global_dtors_aux_fini_array_entry
0000000000201008 D __dso_handle
0000000000200d88 t __frame_dummy_init_array_entry
0000000000200d90 t __init_array_end
0000000000200d88 t __init_array_start
0000000000000b90 T __libc_csu_fini
0000000000000b20 T __libc_csu_init
0000000000201010 D _edata
0000000000201018 B _end
0000000000000b94 T _fini
0000000000000660 T _init
0000000000000710 T _start
0000000000201010 b completed.7696
0000000000201000 W data_start
0000000000000740 t deregister_tm_clones
0000000000000810 t frame_dummy
0000000000000a92 T main
000000000000081a T function_I_would_like_to_use \\ << this one
0000000000000780 t register_tm_clones

Why does dlsym fail to find a function in a dynamically loaded library?

Following this answer to the question about compiling additional code at runtime in C or C++ I prepared this main program
#include <dlfcn.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
int main()
{
// create library
system ( "/usr/bin/gcc -fPIC -shared test.cpp -o libtest.so" );
// load library
void* fLib = dlopen ( "./libtest.so", RTLD_LAZY );
if ( !fLib ) {
cerr << "Cannot open library: " << dlerror() << '\n';
}
if ( fLib ) {
void ( *fn ) () = reinterpret_cast<void (*)()>(dlsym(fLib, "test"));
if ( fn ) {
fn();
}
dlclose ( fLib );
}
return 0;
}
That is supposed to compile "test.cpp" into a dynamic library libtest.so in the current directory, load the library, find the test function in the library and call this funciton, but it seems that dlsym can't find the function, even though it's there.
Here's the test.cpp file:
#include <iostream>
void test()
{
std::cout << "TEST" << std::endl;
}
The main.cpp is compiled with
g++ -ldl main.cpp -o main
and when I execute it, nothing happens: the library is loaded but the fn pointer is not available, meaning the function hasn't been found in libtest.so.
If I look at the symbols in libtest.so with nm -gD, I see the test function:
nm -gD libtest.so
U __cxa_atexit##GLIBC_2.2.5
w __cxa_finalize##GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000001139 T _Z4testv
U _ZNSolsEPFRSoS_E
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
and
c++filt _Z4testv
test()
Why doesn't dlsym find test()? How can I make it find the test function?
The solution is rather simple: Use C language linkage for exported symbols. Then they won't be mangled:
extern "C" void test()
{
std::cout << "TEST" << std::endl;
}

Rcpp - Compiling outside the structure of a package

I'm having a question about using a C ++ code using Rcpp outside of a package structure.
To clarify my doubt, consider the C ++ code (test.cpp) below:
// [[Rcpp::depends(RcppGSL)]]
#include <Rcpp.h>
#include <numeric>
#include <gsl/gsl_sf_bessel.h>
#include <RcppGSL.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector timesTwo(NumericVector x) {
return x * 2;
}
// [[Rcpp::export]]
double my_bessel(double x){
return gsl_sf_bessel_J0 (x);
}
// [[Rcpp::export]]
int tamanho(NumericVector x){
int n = x.size();
return n;
}
// [[Rcpp::export]]
double soma2(NumericVector x){
double resultado = std::accumulate(x.begin(), x.end(), .0);
return resultado;
}
// [[Rcpp::export]]
Rcpp::NumericVector colNorm(const RcppGSL::Matrix & G) {
int k = G.ncol();
Rcpp::NumericVector n(k); // to store results
for (int j = 0; j < k; j++) {
RcppGSL::VectorView colview = gsl_matrix_const_column (G, j);
n[j] = gsl_blas_dnrm2(colview);
}
return n; // return vector
}
The above code works when it is inside the structure of a package. As we know, using the Rcpp::compileAttributes() the file is created RcppExports.cpp. So I will have access to the functions in the R. environment.
My interest is to use the C++ function implemented using the Rcpp outside the framework of a package. For this I compiled the C ++ code using the g ++ compiler as follows:
g++ -I"/usr/include/R/" -DNDEBUG -I"/home/pedro/R/x86_64-pc-linux-gnu-library/3.5/Rcpp/include" -I"/home/pedro/Dropbox/UFPB/Redes Neurais e Análise de Agrupamento/Rcpp" -I /home/pedro/R/x86_64-pc-linux-gnu-library/3.5/RcppGSL/include -D_FORTIFY_SOURCE=2 -fpic -march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong -fno-plt -c test.cpp -o test.o -lgsl -lgslcblas -lm
g++ -shared -L/usr/lib64/R/lib -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -o produto.so produto.o -L/usr/lib64/R/lib -lR -lgsl -lgslcblas -lm
The compilation occurred successfully and no warning message was issued. In this way, the test.o andtest.so files were generated. Already in R, using the .Call interface, I did:
dyn.load("test.so")
my_function <- function(x){
.Call("soma2",x)
}
When trying to use the my_function () function, an error occurs stating that soma2 is not in the load table. Is there any way to create the RcppExports.cpp file outside the framework of a package? I guess the correct one would be to have compiled the code RcppExports.cpp and not test.cpp.
Thanks in advance.
If you are working outside of a package you can simply use Rcpp::sourceCpp(<file>). This will take care of compilating, linking and prociding an R wrapper for you. With your file I get:
> Rcpp::sourceCpp("test.cpp")
> soma2(1:5)
[1] 15

how static library link

In lib.a def:
main.cpp
#include "fun_a.h"
void fun_a() {
return;
}
void fun_nocall_a(){
return;
}
g++ main.cpp -L./ -la
to my understanding,a.out should not link fun_nocall_a, but nm output:
00000000006008e0 d _DYNAMIC 0000000000600aa8 d _GLOBAL_OFFSET_TABLE_ 0000000000400738 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses 000000000040061b T _Z12fun_nocall_av 0000000000400635 T _Z12fun_nocall_bv 00000000004005f8 T _Z15fun_nocall_mainv
It has link fun_nocall_a;
another test:
#include "fun_a.h"
#include "hiredis.h"
void fun_nocall_main() {
return;
}
int main() {
redisConnect(NULL,0);
fun_a();
return 0;
}
Use Redis lib, I find that some function linked by a.out, but some not;
0000000000402e70 T redisAppendCommand
0000000000401b80 T redisAppendCommandArgv
00000000004017e0 T redisAppendFormattedCommand
0000000000401de0 T redisBufferRead
0000000000402240 T redisBufferWrite
0000000000406fa0 T redisCheckSocketError
0000000000404050 T redisCommand
0000000000403fd0 T redisCommandArgv
0000000000402050 T redisConnect
00000000004021b0 T redisConnectBindNonBlock
0000000000401ee0 T redisConnectFd
0000000000401390 T redisConnectNonBlock
0000000000402140 T redisConnectUnix
my question is :
what's principle when link static library?

Compiling simple Opt++ example: Undefined reference to dswap_

I downloaded Opt++. I did the basic install:
1. cd optpp-2.4
2. ./configure
3. make >& make.log
4. make check >& makecheck.log
5. make install
I replicated Example 1: Unconstrained Quasi-Newton Without Derivatives in the codes below. But as I try to compile, i.e.
g++ run_rosen.cpp rosen.cpp -Wno-write-strings -DHAVE_NAMESPACES -I/usr/local/include -I/usr/local/lib -lopt -lnewmat
I get the errors below. And I also think it's odd that there's a reference to my downloads directory.
//usr/local/lib/libopt.a(dqrdc.o): In function dqrdc:
/home/a/downloads/optpp-2.4/src/PDS/dqrdc.c: 156: undefind reference to dswap_
/home/a/downloads/optpp-2.4/src/PDS/dqrdc.c: 182: undefind reference to dswap_
...
//usr/local/lib/libopt.a(pdsdone.o): In function pdsdone:
/home/a/downloads/optpp-2.4/src/PDS/pdsdone.c: 128: undefind reference to dnrm2_
...
Codes:
run_rosen.cpp
#include <fstream>
#include "rosen.h"
#include "NLF.h"
#include "OptQNewton.h"
using namespace OPTPP;
int main()
{
int ndim = 2;
FDNLF1 nlp (ndim, rosen, init_rosen);
OptQNewton objfcn(&nlp);
objfcn.setSearchStrategy(TrustRegion);
objfcn.setMaxFeval(200);
objfcn.setFcnTol(1.e-4);
if (!objfcn.setOutputFile("rosen.out", 0))
{
cerr << "main: output file open failed" << endl;
}
objfcn.optimize();
objfcn.printStatus("Solution from quasi-newton");
objfcn.cleanup();
}
rosen.h
#ifndef ROSEN_H
#define ROSEN_H
#include "NLF.h"
#include "OptNewton.h"
void init_rosen(int ndim, NEWMAT::ColumnVector& x);
void rosen(int ndim, const NEWMAT::ColumnVector& x, double& fx, int& result);
#endif // ROSEN_H
rosen.cpp
#include "rosen.h"
void init_rosen(int ndim, NEWMAT::ColumnVector& x)
{
if (ndim != 2)
{
exit(1);
}
x(1) = 1.2;
x(2) = 1.0;
}
void rosen(int ndim, const NEWMAT::ColumnVector& x, double& fx, int& result)
{
if (ndim != 2)
{
exit(1);
}
double f1, f2;
f1 = (x(2) - x(1) * x(1));
f2 = 1. - x(1);
fx = 100. * f1 * f1 + f2 * f2;
result = OPTPP::NLPFunction;
}
You need to link against a BLAS library. Try adding this to the end of your build command:
-lblas