Writing C++ Shared Libraries for Linux

I've recently been getting an old Windows-only module cross compiling for Linux. One of the challenges has been figuring out how to make the consuming project find & properly open the .so file. Most posts suggest that you must put your libraries in a specific folder, but a developer friend with stronger Linux kung-fu than mine confirmed that you can simply drop the .so files in the same directory as your application, & consume them using dlopen.

Here's a couple examples: first, a trivial hello world to show the basic consumption, and second, an example of what a reusable interface might look like. The latter shows how to use custom parameter & return types. I am new to designing C interfaces and deploying on Linux, so any comments are welcome.

Hello World from a Linux shared library:
cmake_minimum_required (VERSION 3.13)
project (ConsumingProject)
add_executable (ConsumingProject "ConsumingProject.cpp" "ConsumingProject.h")
if (UNIX)
target_link_libraries(ConsumingProject "${CMAKE_DL_LIBS}") #Allows us to include dlfcn.h to link shared (dynamic) libraries
endif()
find_library(SHARED_LIB_LOCATION SharedLib PATHS ${CMAKE_BINARY_DIR}) #Assumes library's .so files have been copied into the same directory as this file
target_link_libraries(ConsumingProject "${SHARED_LIB_LOCATION}")
cmake_minimum_required(VERSION 3.13) #3.13 was what I had on my WSL install.
project(SharedLib VERSION 0)
add_library(SharedLib SHARED "")
set_target_properties(SharedLib PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(SharedLib PROPERTIES SOVERSION 1) #API Version
target_sources(SharedLib
PUBLIC
SharedLib.h
SharedLib.cpp
InterfaceTypes.h
)
#include "ConsumingProject.h"
#include <dlfcn.h>
#include <iostream>
int main()
{
void* handle = dlopen("libSharedLib.so", RTLD_NOW);
if (!handle)
{
std::cout << dlerror() << std::endl;
}
void (*HelloWorld)() = (void (*)())dlsym(handle, "HelloWorld");
char* error = dlerror();
if (nullptr != error)
{
std::cout << error << std::endl;
}
HelloWorld();
dlclose(handle);
return 0;
}
#include "SharedLib.h"
#include <iostream>
void HelloWorld()
{
std::cout << "Hello World" << std::endl;
}
view raw SharedLib.cpp hosted with ❤ by GitHub
#pragma once
extern "C"
{
void HelloWorld();
}
view raw SharedLib.h hosted with ❤ by GitHub


Passing an interface from a Linux shared library:
cmake_minimum_required (VERSION 3.13)
project (ConsumingProject)
add_executable (ConsumingProject "ConsumingProject.cpp" "ConsumingProject.h")
if (UNIX)
target_link_libraries(ConsumingProject "${CMAKE_DL_LIBS}") #Allows us to include dlfcn.h to link shared (dynamic) libraries
endif()
find_library(SHARED_LIB_LOCATION SharedLib PATHS ${CMAKE_BINARY_DIR}) #Assumes library's .so files have been copied into the same directory as this file
target_link_libraries(ConsumingProject "${SHARED_LIB_LOCATION}")
cmake_minimum_required(VERSION 3.13) #3.13 was what I had on my WSL install.
project(SharedLib VERSION 0)
add_library(SharedLib SHARED "")
set_target_properties(SharedLib PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(SharedLib PROPERTIES SOVERSION 1) #API Version
target_sources(SharedLib
PUBLIC
SharedLib.h
SharedLib.cpp
InterfaceTypes.h
)
#include "ConsumingProject.h"
#include "InterfaceTypes.h"
#include <dlfcn.h>
#include <iostream>
int main()
{
void* handle = dlopen("libSharedLib.so", RTLD_NOW);
if (!handle)
{
std::cout << dlerror() << std::endl;
}
MyInterface theInterface;
theInterface.GetCustomReturnType = (MyCustomReturnType(*)(MyCustomParameterType))dlsym(handle, "GetCustomReturnType");
MyCustomParameterType param = 1;
MyCustomReturnType returnStruct = theInterface.GetCustomReturnType(param);
char* error = dlerror();
if (nullptr != error)
{
std::cout << error << std::endl;
}
else
{
std::cout << "Return value: " << returnStruct.Result << std::endl;
}
dlclose(handle);
return 0;
}
#pragma once
extern "C"
{
typedef long MyCustomParameterType;
typedef struct MyCustomReturnType
{
long Result;
} MyCustomReturnType;
typedef struct MyInterface
{
MyCustomReturnType (*GetCustomReturnType)(MyCustomParameterType);
} MyInterface;
}
#include "SharedLib.h"
#include <iostream>
MyCustomReturnType GetCustomReturnType(MyCustomParameterType param)
{
std::cout << "Param value: " << param << std::endl;
MyCustomReturnType returnStruct;
returnStruct.Result = 123;
return returnStruct;
}
view raw SharedLib.cpp hosted with ❤ by GitHub
#pragma once
#include "InterfaceTypes.h"
extern "C"
{
MyCustomReturnType GetCustomReturnType(MyCustomParameterType param);
}
view raw SharedLib.h hosted with ❤ by GitHub

Comments

Popular posts from this blog

Fixing Conan Lock Issues

Initialize With Care

Dude, Where's My Framework?