Skip to content

Instantly share code, notes, and snippets.

@caiorss
Last active March 30, 2021 21:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caiorss/2bba4c50866d9467aaa8c7792b337f71 to your computer and use it in GitHub Desktop.
Save caiorss/2bba4c50866d9467aaa8c7792b337f71 to your computer and use it in GitHub Desktop.
Sample Windows DLL - shared library testlib.dll and client program (client code). - https://caiorss.github.io/C-Cpp-Notes/DLL-Binary-Components-SharedLibraries.html
*.dll
*.exe
*.obj
*.so
*.bin
*.zip
*.7z
*.exp
*.lib
.vs/*
@echo off
@echo Automate CMake initialization.
@echo Building project
:: Alias to executable
set CMAKEBIN="C:\Program Files\CMake\bin\cmake"
@echo Setting up project
@echo --------------------------------------------------
:: Generate specific preferred project for current platform
"C:\Program Files\CMake\bin\cmake" -H. -Bbuild -G "Visual Studio 15 2017 Win64"
@echo Executing Install Target
@echo --------------------------------------------------
:: Build project and copy executable and dll to this directory
"C:\Program Files\CMake\bin\cmake" --build build --config install
:: "C:\Program Files\CMake\bin\cmake" --build build --config Release -- install VERBOSE=1
@echo Compilation finished
::-----------------------------------------------::
:: Wait user enter any key to exit
pause
@echo off
rem Compile for x86 or x64 bits
rem ------------------------------
rem set MODE=x86
set MODE=x64
@REM Visual studio building tools path - Install it with chocolately
set VS2017="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
set VS2015="C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat"
rem Save current directory
pushd %CD%
@REM Set visual Studio 2017
call %VS2017% %MODE%
@REM Restore saved directory
popd
@REM ------------------ User Command Goes Here ----------------- @REM
@REM Build solution in Debug mode
cl.exe testlib.cpp /EHsc /LD /nologo user32.lib
cl.exe /EHsc client1.cpp /Fe:client1.exe testlib.lib && client1.exe
@REM Set /p Wait=Build Process Completed...
pause
// File: client-csharp.cs
// Brief: C# client code (via P-invoke) for the DLL Shared library.
// Author: Caio Rodrigues
//-----------------------------------------------------------------
using System;
using System.Runtime.InteropServices;
class DllCsharpClient{
static void Main(string[] args){
Console.WriteLine(" ===== EXPERIMENT 1 = Using C-functions directly ==");
Console.WriteLine(" [CSharp] Loading Native DLL C++ Shared Library");
IntPtr vectorObject = VectorD.testlib_vectorD_make0(5, 3.0);
Console.WriteLine(" [CSharp] Opaque pointer = ", vectorObject.ToString());
VectorD.testlib_vectorD_Linalg_printVector("vectorX", vectorObject);
double x = VectorD.testlib_vectorD_Linalg_norm(vectorObject);
Console.WriteLine(" [CSharp] Vector norm = " + x.ToString());
Console.WriteLine(" [CSharp] End application");
VectorD.testlib_vectorD_delete(vectorObject);
Console.WriteLine(" ===== EXPERIMENT 2 = Using OOP wrapper for std::vector ===");
Console.WriteLine(" ==> Before changing vector");
VectorD v1 = new VectorD(10, 3.5);
v1.print();
v1.print("vector_v1");
Console.WriteLine("v1.norm() = " + v1.norm().ToString());
Console.WriteLine(" ==> After changing vector");
v1.set(1, 10.0); v1.set(2, 5.53); v1.set(3, 8.96);
v1.set(4, -10.34); v1.set(8, 80.54);
v1.print("v1_changed");
Console.WriteLine(" ==> Creating Vector from Array");
VectorD v2 = new VectorD(new double[] {4.5, -8.84, 78.23, 652.3, 34.56, 45.12});
v2.print("v2");
Console.WriteLine("v2.norm() = " + v2.norm().ToString());
Console.WriteLine(" ===== EXPERIMENT 3 = OOP wrapper for InterfaceClass ===");
CPPInterfaceClass instA = CPPInterfaceClass.ImplementationA();
CPPInterfaceClass instB = CPPInterfaceClass.ImplementationB();
// Console.WriteLine("instA.Name = " + instA.Name);
Console.WriteLine("instA.GetID() = " + instA.GetID());
Console.WriteLine("instB.GetID() = " + instB.GetID());
Console.WriteLine(" **=> Before changing");
Console.WriteLine("instA.Name = " + instA.Name);
Console.WriteLine("instB.Name = " + instB.Name);
Console.WriteLine("\n **=> Before changing");
instA.Name = "Instance-of-ImplA";
instB.Name = "Instance-of-ImplB";
Console.WriteLine("instA.Name = " + instA.Name);
Console.WriteLine("instB.Name = " + instB.Name);
Console.WriteLine(" ===== END ===========================");
try {
Console.ReadKey();
}
catch(System.InvalidOperationException ex)
{
}
}
}
class VectorD{
// Constructor Function:
// hVectorD testlib_vectorD_make0(size_t n, double x)
// void* testlib_vectorD_make0(size_t n, double x)
[DllImport("testlib.dll")]
public static extern
IntPtr testlib_vectorD_make0(int size, double x);
[DllImport("testlib.dll")]
public static extern
IntPtr testlib_vectorD_make1(int n, double[] x);
// Destructor function:
// void testlib_vectorD_delete(hVectorD hv)
// void testlib_vectorD_delete(void* hv)
[DllImport("testlib.dll")]
public static extern
IntPtr testlib_vectorD_delete(IntPtr hv);
// void testlib_vectorD_Linalg_printVector(const char* name, hVectorD hv)
// void testlib_vectorD_Linalg_printVector(const char* name, vector* hv)
[DllImport("testlib.dll")]
public static extern
void testlib_vectorD_Linalg_printVector(string name, IntPtr hv);
// double testlib_vectorD_Linalg_norm(hVectorD hv)
// double testlib_vectorD_Linalg_norm(void* hv)
[DllImport("testlib.dll")]
public static extern
double testlib_vectorD_Linalg_norm(IntPtr hv);
// void testlib_vectorD_set(hVectorD hv, size_t n, double x)
// void testlib_vectorD_set(void* hv, size_t n, double x)
[DllImport("testlib.dll")]
public static extern
void testlib_vectorD_set(IntPtr hv, int n, double x);
// ------------ Objet Oriented Wrapper --------//
// Hadle
private IntPtr m_handle;
public VectorD(int size, double x){
m_handle = testlib_vectorD_make0(size, x);
}
public VectorD(double[] array){
m_handle = testlib_vectorD_make1(array.Length, array);
}
// Finalized destructor
~VectorD(){
testlib_vectorD_delete(m_handle);
}
public void print(string name = "std::vector<double>"){
testlib_vectorD_Linalg_printVector(name, m_handle);
}
public void set(int n, double x){
testlib_vectorD_set(m_handle, n, x);
}
public double norm(){
return testlib_vectorD_Linalg_norm(m_handle);
}
}
/* Wrapper for the interface C++ interface class 'InterfaceClass'
*
*/
class CPPInterfaceClass{
// Handle type => Opaque pointer for InterfaceClass instances.
// using HInterf = IntPtr;
// Factory function for loading functions from this interface.
//-----------------------------------------------------------
// InterfaceClass* teslib_InterfaceClass_factory(const char* class_id)
// void** teslib_InterfaceClass_factory(const char* class_id)
[DllImport("testlib.dll")]
private static extern
IntPtr teslib_InterfaceClass_factory(string class_id);
// void testlib_InterfaceClass_delete(InterfaceClass* hinst)
// void testlib_InterfaceClass_delete(void*)
[DllImport("testlib.dll")]
private static extern
void testlib_InterfaceClass_delete(IntPtr hinst);
// char* testlib_InterfaceClass_getID(InterfaceClass* hinst)
[DllImport("testlib.dll")]
private static extern IntPtr testlib_InterfaceClass_getID(IntPtr hinst);
// void testlib_InterfaceClass_setName(InterfaceClass* hinst, const char* name)
// Note: Charset.UNICODE does not work as it is UTF16 (Wide Unicode), not UTF-8
// which is the C++ default unicode type.
[DllImport("testlib.dll", CharSet = CharSet.Ansi)]
private static extern
void testlib_InterfaceClass_setName(IntPtr hinst, string name);
// char* testlib_InterfaceClass_getName(InterfaceClass* hinst)
[DllImport("testlib.dll")]
private static extern
IntPtr testlib_InterfaceClass_getName(IntPtr hinst);
private IntPtr m_handle;
private CPPInterfaceClass(IntPtr handle){
m_handle = handle;
}
~CPPInterfaceClass(){
testlib_InterfaceClass_delete(m_handle);
}
/** Creates an instance of the the C++ implementation ImplementationA (class) from the
* C++ interface: 'InterfaceClass'
*/
public static CPPInterfaceClass ImplementationA(){
return new CPPInterfaceClass(teslib_InterfaceClass_factory("ImplementationA"));
}
/** Creates an instance of the C++ implementationa ImplementationB (class) */
public static CPPInterfaceClass ImplementationB(){
return new CPPInterfaceClass(teslib_InterfaceClass_factory("ImplementationB"));
}
// Get type of the wrapped C++ class
public string GetID(){
IntPtr p = testlib_InterfaceClass_getID(m_handle);
return Marshal.PtrToStringAnsi(p);
}
// Get/Set name of current instance
public string Name
{
get{
IntPtr p = testlib_InterfaceClass_getName(m_handle);
return Marshal.PtrToStringAnsi(p);
}
set{
testlib_InterfaceClass_setName(m_handle, value);
}
}
}
// File: client.cpp
// Brief: Sample C++ client code for DLL testlib.dll
// Author: Caio Rodrigues
//---------------------------------------------------------------
#include <iostream>
#include <ostream>
#include <vector>
#include <string>
#include "testlib.hpp"
#define WindbgTrace(text) \
{ std::stringstream ss; \
ss << __FILE__ << ":" << __LINE__ << ": <" << __FUNCTION__ "> " \
<< text << std::endl;\
OutputDebugString(ss.str().c_str()); \
}
#define DbgTrace(text) \
{ std::cerr << __FILE__ << ":" << __LINE__ << ": <" << __FUNCTION__ << "> " \
<< text << std::endl; }
#define DbgDisp(expr) \
{ std::cerr << __FILE__ << ":" << __LINE__ << ": <" << __FUNCTION__ << "> " \
<< #expr << " = " << (expr) << std::endl; }
extern "C" hVectorD testlib_vectorD_make0(size_t n, double);
extern "C" hVectorD testlib_vectorD_make1(size_t n, double array []);
extern "C" void testlib_vectorD_delete(hVectorD hv);
int main(){
#ifndef DISABLE
std::cout << "\n=== EXPERIMENT 1 ===> Import C++ functions from DLL" << std::endl;
DbgTrace("Main process starts here.");
std::vector<double> xs{1.0, 2.0, 3.0, 4.0, 5.0};
std::cout << " => Linalg::norm(xs) " << Linalg::norm(xs) << std::endl;
std::cout << "=> xs = "; Linalg::printVector(std::cout, xs); std::cout << std::endl;
std::cout << "=== EXPERIMENT 2 ===> Import class from DLL" << std::endl;
auto cls = SampleClass("Dummy");
cls.set(100);
std::cout << "cls.getName() = " << cls.getName() << std::endl;
std::cout << " cls.get() = " << cls.get() << std::endl;
#endif // -- eof DISABLE flag
//=========>> Load functions and classes using C-interface ==============//
std::cout << "\n== EXPERMIMENT 3 ===> Import C-functions from DLL - C-interface" << std::endl;
double arr [] = {1, 2, 3, 4, 5};
hVectorD v1 = testlib_vectorD_make1(5, arr);
testlib_vectorD_Linalg_printVector("v1", v1);
std::cout << "norm(v1) = " << testlib_vectorD_Linalg_norm(v1) << std::endl;
testlib_vectorD_delete(v1);
std::cout << "\n== EXPERMIMENT 4 ===> Non-polymorphic class with C-interface " << std::endl;
hSampleClass hcls = testlib_SampleClass_make1("[EXPERIMENT4]ClassHandle-OOP-C-API");
std::cout << "[EXPERIMENT 4] hcls.getName() = " << testlib_SampleClass_getName(hcls) << std::endl;
testlib_SampleClass_set(hcls, 100);
std::cout << "[EXPERIMENT 4] hcls.get() = " << testlib_SampleClass_get(hcls) << std::endl;
testlib_SampleClass_set(hcls, 200);
std::cout << "[EXPERIMENT 4] hcls.get() = " << testlib_SampleClass_get(hcls) << std::endl;
testlib_SampleClass_delete(hcls);
std::cout << "\n== EXPERMIMENT 5 ===> Load polymorphic classes from DLL " << std::endl;
InterfaceClass* hinstA = teslib_InterfaceClass_factory("ImplementationA");
InterfaceClass* hinstB = teslib_InterfaceClass_factory("ImplementationB");
std::cout << " => hinstA->getID() = " << hinstA->getID() << std::endl;
std::cout << " => hinstA->getID() = " << hinstB->getID() << std::endl;
hinstA->setName("ClassA-implA");
hinstB->setName("ClassB-implB");
std::cout << " => hinstA->getName() = " << hinstA->getID() << std::endl;
std::cout << " => hinstB->getName() = " << hinstB->getID() << std::endl;
std::cout << " [INFO] hinstA->getID() = " << hinstA->getID() << std::endl;
// Note: If delete is used directly to delete hinstA and hinstB,
// a segmentatin fault will happen whenc compiling with Mingw/GCC
testlib_InterfaceClass_delete(hinstA);
testlib_InterfaceClass_delete(hinstB);
std::cout << " [INFO] After deleting instances" << std::endl;
DbgTrace("Program ended OK.");
#if defined(_WIN32)
std::cout << "\n ** Enter RETURN to exit" << std::endl;
std::cin.get();
#endif
return 0;
}
# Description: Minimal CMake for building shared libraries in a cross platform way.
# Author: Caio Rodrigues
# Tested on: Linux, Windows 10 with MSVC/MSBuild and Mingw Makefiles and NMake JOM.
#==================================================================
cmake_minimum_required(VERSION 3.9)
set(CMAKE_VERBOSE_MAKEFILE ON)
project(My_shared_library)
# Set C++ 14 Standard
set(CMAKE_CXX_STANDARD 14)
# Set default building type to debug if it was not set
# in command line with -DCMAKE_BUILD_TYPE=release
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES "")
set(CMAKE_BUILD_TYPE debug)
endif()
message(" [INFO] Build type => CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
message(" [INFO] 64 bits build.")
else()
message(" [INFO] 32 bits build.")
endif()
#====== Shared library target (testlib) =================#
# File name =>
add_library(
testlib # Target Name
SHARED # Specify that target is a shared library
./testlib.cpp
# src/source2.cpp
# src/source3.cpp
)
set_target_properties(
# Remove prexix 'lib' from file name, so instead of generating
# the file liblibtest.so, it will build the file liblibtest.so.
#-----------------------------------------
# testlib PROPERTIES PREFIX ""
# TL;DR => -fvisibility=hidden for this library (testlib.so or testlib.dll).
#
# Make library symbols hidden by default as it happens in Windows were
# exported symbols are required be explicitly annotated
# with __declspec(dllexport) or __declspec(dllimport).
# On U-nix-like OSes (Linux, BSD, OSX ...), it will add the compilation flag
# -fvisibility=hidden
# Reference: https://stackoverflow.com/questions/17080869
#--------------------------------------------------------------------
testlib PROPERTIES CXX_VISIBILITY_PRESET hidden
# testlib PROPERTIES OUTPUTT_NAME "someOtherName"
# mylib PROPERTIES VERSION ${PROJECT_VERSION}
)
# Include current directory (.) PWD
# target_include_directories(testlib PRIVATE .)
# Post Target Build. -- Event target
#---------------------------------------
# Automatically copies the target testlib (testlib.so) to
# project root directory whenever this target is rebuilt.
# Ref: https://stackoverflow.com/questions/31277186
# Ref: https://stackoverflow.com/questions/15694347/
add_custom_command(TARGET testlib POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:testlib>
${CMAKE_CURRENT_SOURCE_DIR}/libtest.so
)
#====== Test executable for loading the library (testlib) =======#
#
add_executable(client1-executable ./client1.cpp)
# Add extension .bin to the built Unix-executable, so it will generate
# the program 'client1-executable.bin' instead of 'client1-executable'
if(UNIX)
set_target_properties(client1-executable PROPERTIES SUFFIX ".bin")
endif()
find_library(libtest_location libtest)
message(" [INFO] Libtest location is at: ${libtest_location}")
target_link_libraries(client1-executable PUBLIC testlib)
# target_link_libraries(client1-executable PUBLIC libtest.so)
# target_compile_definitions(client1-executable PUBLIC libtest)
#target_compile_options(client1-executable PUBLIC testlib)
# If not set the install directory, attemp set the install directory
# CMAKE_INSTALL_PREFIX as this directory
if(NOT DEFINED CMAKE_INSTALL_PREFIX OR CMAKE_INSTALL_PREFIX MATCHES "")
set(CMAKE_INSTALL_PREFIX ".")
endif()
message(" [INFO] CMakeLists.txt is in the directory ${CMAKE_CURRENT_LIST_DIR}")
# Copy targets to ./ - Directory where is this file CMakeLists.txt (project top level dir)
install(TARGETS client1-executable testlib DESTINATION ${CMAKE_CURRENT_LIST_DIR}/bin)
add_custom_target(run
COMMAND client1-executable
DEPENDS client1-executable
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
)
all: build
# Generate build script
cmake:
cmake -H. -Bbuild
compile:
cmake --build build --config Release -- VERBOSE=1
# Compile
build:
cmake -H. -Bbuild -DCMAKE_INSTALL_PREFIX=. && \
cmake --build build --config Release -- install VERBOSE=1
run:
build/client1-executable.bin
linux:
echo "Build shared library"
g++ testlib.cpp -o libtestlib.so -std=c++14 -fPIC -shared -Wall
echo "Build client code 1"
g++ client1.cpp -o client1.bin -std=c++14 libtestlib.so
# Run python client code.
py:
python3 pywrapper.py
# Clean ./build directory
clean:
rm -rf -v ./build
#===========================================================================
# File: pywrapper.py
# Brief: Python wrapper for the DLL - Shared Library testlib.dll
# Python Version: 3.0
# Author: Caio Rodrigues
#=========================================================================
import ctypes
def _getSharedLibrary(libname):
import sys
import os
libfile = libname
if sys.platform == "linux" or sys.platform == "linux2":
libfile = "lib" + libname + ".so"
elif sys.platform == "darwin":
libfile = libname + ".dylyb"
elif sys.platform == "win32":
libfile = libname + ".dll"
libpath = os.path.join(os.path.dirname(__file__), libfile)
print(" [INFO] libpath = " + libpath)
return libpath
# _lib = ctypes.cdll.LoadLibrary("testlib")
_lib = ctypes.cdll.LoadLibrary(_getSharedLibrary("testlib"))
# Startup ctypes FFI - Foreign Function Interface
def _config():
print("Intializing library")
# ======= std::vector<double> and Linalg:: namespace ==========##
# hVectorD testlib_vectorD_make0(size_t n, double x)
_lib.testlib_vectorD_make0.argtypes = [ctypes.c_int, ctypes.c_double]
_lib.testlib_vectorD_make0.restype = ctypes.c_void_p
# hVectorD testlib_vectorD_make1(size_t n, double array [])
_lib.testlib_vectorD_make1.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_double)]
_lib.testlib_vectorD_make1.restype = ctypes.c_void_p
# void testlib_vectorD_Linalg_printVector(const char* name, hVectorD hv)
_lib.testlib_vectorD_Linalg_printVector.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.c_void_p ]
_lib.testlib_vectorD_Linalg_printVector.restype = None
# Set vector elements hv[n] = x
# void testlib_vectorD_set(hVectorD hv, size_t n, double x)
_lib.testlib_vectorD_set.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_double]
_lib.testlib_vectorD_set.restype = None
# double Linalg::norm(const std::vector<double>& xs)
# double testlib_vectorD_Linalg_norm(hVectorD hv)
_lib.testlib_vectorD_Linalg_norm.argtypes = [ ctypes.c_void_p ]
_lib.testlib_vectorD_Linalg_norm.restype = ctypes.c_double
# Function: void testlib_vectorD_delete(hVectorD hv)
_lib.testlib_vectorD_delete.argtypes = [ ctypes.c_void_p ]
_lib.testlib_vectorD_delete.restype = None
#======== C-wrappers for function interface class InterfaceClass =============#
# InterfaceClass* teslib_InterfaceClass_factory(const char* class_id)
_lib.teslib_InterfaceClass_factory.argtypes = [ ctypes.POINTER(ctypes.c_char) ]
_lib.teslib_InterfaceClass_factory.restype = ctypes.c_void_p
# const char* testlib_InterfaceClass_getID(InterfaceClass* hinst)
_lib.testlib_InterfaceClass_getID.argtypes = [ ctypes.c_void_p ]
_lib.testlib_InterfaceClass_getID.restype = ctypes.POINTER(ctypes.c_char)
# const char* testlib_InterfaceClass_getName(InterfaceClass* hinst)
_lib.testlib_InterfaceClass_getName.argtypes = [ ctypes.c_void_p ]
_lib.testlib_InterfaceClass_getName.restype = ctypes.POINTER(ctypes.c_char)
# void testlib_InterfaceClass_setName(InterfaceClass* hinst, const char* name)
_lib.testlib_InterfaceClass_setName.argtypes = [ ctypes.c_void_p, ctypes.POINTER(ctypes.c_char) ]
_lib.testlib_InterfaceClass_setName.restype = None
print("Library initialized OK.")
# Initializae module.
_config()
class VectorD:
def __init__(self, handle):
self.hnd = ctypes.c_void_p(handle)
self.name = "std::vector<double> vx"
@classmethod
def fromValue(cls, size, x):
return VectorD(_lib.testlib_vectorD_make0(size, x))
@classmethod
def fromArray(cls, array):
carray_size_n = ctypes.c_double * len(array)
return VectorD(_lib.testlib_vectorD_make1(len(array), carray_size_n(*array)))
# Destructor
def __del__(self):
print(" [TRACE] - Vector disposed - C++ Destructor invoked Ok.")
_lib.testlib_vectorD_delete(self.hnd)
def setName(self, name):
self.name = name
# Display vector
def disp(self):
_lib.testlib_vectorD_Linalg_printVector(self.name.encode('utf-8'), self.hnd)
# Set element at nth position
def set(self, idx, x):
_lib.testlib_vectorD_set(self.hnd, idx, x)
def norm(self):
return _lib.testlib_vectorD_Linalg_norm(self.hnd)
# Proxy for C++ Interface class in the shared library
class CPPInterfaceClass:
# Constructor
def __init__(self, handle):
self.hnd = ctypes.c_void_p(handle)
# Destructor
def __del__(self):
# Call C++ destructor
# print(" [__del__] => self.hnd = " + str(self.hnd))
_lib.testlib_InterfaceClass_delete(self.hnd)
@classmethod
def factory(cls, classID):
return CPPInterfaceClass(_lib.teslib_InterfaceClass_factory(classID.encode('utf-8')))
@classmethod
def makeA(cls):
"Instantiate the class ImplementationA from the DLL."
return CPPInterfaceClass.factory("ImplementationA")
@classmethod
def makeB(cls):
"Instantiate the class ImplementationB from the DLL."
return CPPInterfaceClass.factory("ImplementationB")
def getType(self):
return ctypes.string_at(_lib.testlib_InterfaceClass_getID(self.hnd)).decode('utf-8')
def getName(self):
return ctypes.string_at(_lib.testlib_InterfaceClass_getName(self.hnd)).decode('utf-8')
def setName(self, name):
_lib.testlib_InterfaceClass_setName(self.hnd, name.encode('utf-8'))
# String representation
def __str__(self):
s = "CInterfaceClass ; type = " + self.getType()
s += " - name = " + self.getName() + "\n"
return s
# Make class printable in the REPL
def __repr__(self):
return self.__str__()
def test1():
print("\n ======== Test 1 - std::vector<double> wrapper and Linalg module ======")
v1 = VectorD.fromValue(4, 3.5)
print(" [*]=> Before changing std::vector<double> object")
v1.disp()
print("v1.norm() = " + str(v1.norm()))
print()
print(" [*]=> After changing std::vector<double> object")
v1.set(0, 5); v1.set(1, 2.6); v1.set(2, 9.81); v1.set(3, 3.76)
v1.disp()
print("v1.norm() " + str(v1.norm()))
print()
print("\n ======== Interface class 'InterfaceClass' ======")
clsA = CPPInterfaceClass.makeA()
print("clsA = " + str(clsA))
print("clsA.getType() = " + clsA.getType())
clsB = CPPInterfaceClass.makeB()
print("clsB = " + str(clsB))
print("clsB.getType() = " + clsB.getType())
if __name__ == "__main__":
test1()
// File: testlib.cpp
// Brief: Sample Windows shared library / DLL implementation
// Author: Caio Rodrigues
//---------------------------------------------------------------
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <sstream>
#include <functional>
#ifdef _WIN32
#include <windows.h>
#else
#define OutputDebugString(msg) std::perror(msg)
#endif
#include "testlib.hpp"
#define WindbgTrace(text) \
{ std::stringstream ss; \
ss << __FILE__ << ":" << __LINE__ << ": <" << __FUNCTION__ << "> " \
<< text << std::endl; \
OutputDebugString(ss.str().c_str()); \
}
#define DbgTrace(text) \
{ std::cerr << __FILE__ << ":" << __LINE__ << ": <" << __FUNCTION__ << "> " \
<< text << std::endl; }
#define DbgDisp(expr) \
{ std::cerr << __FILE__ << ":" << __LINE__ << ": <" << __FUNCTION__ << "> " \
<< #expr << " = " << (expr) << std::endl; }
/** - DLL Entry point - main function of DLL which is executed when
the DLL is loaded by some process.
*/
#if defined(_WIN32)
extern "C" __declspec(dllexport)
BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID lpReserved)
{
std::string text =
std::string("DLL Loaded into the process => PID = ")
+ std::to_string(::GetCurrentProcessId());
WindbgTrace(text);
DbgTrace(text);
switch (reason)
{
case DLL_PROCESS_ATTACH:
WindbgTrace("DLL attached to process");
DbgTrace("DLL attached to process.");
break;
case DLL_PROCESS_DETACH:
WindbgTrace("=> DLL detached.");
DbgTrace("=> DLL attached");
break;
case DLL_THREAD_ATTACH:
WindbgTrace("DLL attached to thread");
DbgTrace("DLL detached to thread.");
break;
case DLL_THREAD_DETACH:
WindbgTrace("DLL detached from thread");
DbgTrace("DLL detached from thread.");
break;
}
return TRUE;
}
#endif
//================================================================//
// Linear algebra tools
namespace Linalg{
EXPORT_CPP
double norm(const std::vector<double>& xs){
double sum = 0.0;
for(const auto& x : xs) sum += x * x;
return std::sqrt(sum);
}
EXPORT_CPP
std::vector<double>
linTransform(double a, double b, std::vector<double>& xs){
std::vector<double> out(xs.size());
for(size_t i = 0; i < xs.size(); i++){
out[i] = a * xs[i] + b;
}
return out;
}
EXPORT_CPP
std::ostream&
printVector(std::ostream& os, std::vector<double>& xs){
os << "[" << xs.size() << "]( ";
for(const auto& x: xs)
os << x << ", ";
return os << " )";
}
}
//=========== C-wrappers ---------------///
// Handler for double vector
using hVectorD = void*;
using pVectorD = std::vector<double>*;
/** C-wrapper for vector<double> constructor */
EXPORT_C
hVectorD testlib_vectorD_make0(size_t n, double x){
return new std::vector<double>(n, x);
}
/** C-wrapper for range constructor */
EXPORT_C
hVectorD testlib_vectorD_make1(size_t n, double array []){
return new std::vector<double>(array, array + n);
}
/** C-wrapper for setting elements of vector<double> */
EXPORT_C
void testlib_vectorD_set(hVectorD hv, size_t n, double x){
static_cast<pVectorD>(hv)->operator[](n) = x;
}
/** C-wrapper for vector<double> destructor */
EXPORT_C void testlib_vectorD_delete(hVectorD hv){
delete reinterpret_cast<pVectorD>(hv);
}
/** C-wrapepr for Linalg::norm function */
EXPORT_C
double testlib_vectorD_Linalg_norm(hVectorD hv){
return Linalg::norm(*reinterpret_cast<pVectorD>(hv));
}
EXPORT_C
void testlib_vectorD_Linalg_printVector(const char* name, hVectorD hv){
std::cout << name << " = ";
Linalg::printVector(std::cout, *reinterpret_cast<pVectorD>(hv));
std::cout << std::endl;
}
//==========>>> class SampleClass <<===========
SampleClass::SampleClass(const std::string& name)
: m_name(name), m_counter(0)
{
std::cout << " Instance created with name = " << m_name << std::endl;
}
/** Delegated constructor on right-hand-side */
SampleClass::SampleClass(): SampleClass("unnamed"){}
SampleClass::~SampleClass(){
std::string text = std::string("SampleClass => name = ") + m_name + " deleted";
DbgTrace(text);
}
std::string SampleClass::getName() const {
return m_name;
}
int SampleClass::SampleClass::get(){
return m_counter;
}
void SampleClass::set(int n){
std::cout << " Counter set to value = " << n << std::endl;
m_counter = n;
}
//======= C-interface of SampleClass ===========//
using hSampleClass = void*;
/** Nullable constructor zero-arg constructor */
EXPORT_C hSampleClass testlib_SampleClass_make0()
{
return new SampleClass();
}
/** Other constructor */
EXPORT_C
auto testlib_SampleClass_make1(const char* name) -> hSampleClass
{
return new SampleClass(name);
}
/** Destructor */
EXPORT_C
auto testlib_SampleClass_delete(hSampleClass hnd) -> void
{
delete reinterpret_cast<SampleClass*>(hnd);
}
/** Wrapper for get method */
EXPORT_C
int testlib_SampleClass_get(hSampleClass hnd)
{
return reinterpret_cast<SampleClass*>(hnd)->get();
}
/** Wrapper for set method */
EXPORT_C
void testlib_SampleClass_set(hSampleClass hnd, int n)
{
return reinterpret_cast<SampleClass*>(hnd)->set(n);
}
EXPORT_C
const char* testlib_SampleClass_getName(hSampleClass hnd)
{
return reinterpret_cast<SampleClass*>(hnd)->getName().c_str();
}
//========= Implementations of the interface class ===//
class ImplementationA: public InterfaceClass
{
private:
std::string m_name;
public:
static constexpr const char* class_id = "ImplementationA";
ImplementationA(): m_name("Unammed-A"){ }
ImplementationA(const std::string& name)
: m_name(name){}
~ImplementationA(){
std::cout << " [INFO] ImplementationA deleted => name = "
<< m_name
<< " ; type = " << class_id
<< std::endl;
}
const char* getID() const {
return class_id;
}
void setName(const char* name) {
m_name = name;
}
const char* getName() {
return m_name.c_str();
}
};
class ImplementationB: public InterfaceClass
{
private:
std::string m_name;
public:
static constexpr const char* class_id = "ImplementationB";
ImplementationB(): m_name("Unammed-B"){ }
ImplementationB(const std::string& name)
: m_name(name){}
~ImplementationB(){
std::cout << " [INFO] ImplementationB deleted => name = "
<< m_name
<< " ; type = " << class_id
<< std::endl;
}
const char* getID() const {
return class_id;
}
void setName(const char* name) {
m_name = name;
}
const char* getName() {
return m_name.c_str();
}
};
EXPORT_C
InterfaceClass*
teslib_InterfaceClass_factory(const char* class_id)
{
auto s = std::string(class_id);
if(s == "ImplementationA")
return new ImplementationA();
if(s == "ImplementationB")
return new ImplementationB();
return nullptr;
}
EXPORT_C void testlib_InterfaceClass_delete(InterfaceClass* hinst)
{
delete hinst;
}
EXPORT_C
const char* testlib_InterfaceClass_getID(InterfaceClass* hinst)
{
return hinst->getID();
}
EXPORT_C
void testlib_InterfaceClass_setName(InterfaceClass* hinst, const char* name)
{
hinst->setName(name);
}
EXPORT_C
const char* testlib_InterfaceClass_getName(InterfaceClass* hinst){
return hinst->getName();
}
///================================================//
/** Static object used for DLL initialization, it is a cross platform
* replacement for the Windows function DLLMain
*
* Class defined in anonymous namespace becomes private to this
* compilation unit - cannot be accessed from any other file
*************************************************************/
namespace {
class _StaticObject{
public:
using Action = std::function<void ()>;
Action m_end;
_StaticObject(Action init, Action end)
: m_end(std::move(end))
{
init();
}
~_StaticObject(){ m_end(); }
_StaticObject(const _StaticObject&) = delete;
_StaticObject& operator=(const _StaticObject&) = delete;
_StaticObject(_StaticObject&&) = default;
_StaticObject& operator=(_StaticObject &&) = default;
};
auto initDLL = _StaticObject(
[]{
std::cout << " [StaticObject] => Initialize DLL"
<< std::endl;
},
[]{
std::cout << " [StaticObject] => Shutdown DLL"
<< std::endl;
});
}
/*** ===========>>>> run32dll Entry Points ================= */
#if defined(_WIN32)
extern "C" __declspec(dllexport)
void entryPoint1(HWND hwn, HINSTANCE hinst, LPSTR cmdLine, int nCmdShow){
DbgDisp(cmdLine);
OutputDebugString("Rudll32 called entryPoint1()");
MessageBoxA(NULL, "DLL ENTRY POINT", "Entry point 1", 0);
}
#endif
extern "C" __declspec(dllexport)
int main(){
std::cout << "Running program OK." << std::endl;
return 0;
}
void NonExportedFunction(){
std::cout << "This function is not visible exported" << std::endl;
}
// File: testlib.hpp
// Brief: Sample Windows shared library / DLL header
// Author: Caio Rodrigues
//------------------------------------------------------
#ifndef _TESTLIB_H_
#define _TESTLIB_H_
#include <ostream>
#include <vector>
#include <string>
/** Macro EXPORT_CPP makes a symbol visible. */
#if defined(_WIN32)
#define EXPORT_CPP __declspec(dllexport)
#else
#define EXPORT_CPP __attribute__ ((visibility ("default")))
// If not compiled on Windows, remove declspec compiler extension.
#ifndef __declspec
// For GCC only - make exported symbol visible symbol
#define __declspec(param) __attribute__ ((visibility ("default")))
#endif
#endif
/* Macro EXPORT_C is used for exporting symbol with C-linkage, it
* means, without name mangling */
#ifdef __cplusplus
// extern "C" - Indicates that a given symbol/function has C-linkage and
// does not have name mangling.
//
// On Linux EXPORT_C becomes
// => extern "C" __attribute__ ((visibility ("default")))
//
// On Windows EXPORT_C becomes
// => extern "C" __declspec(dllexport)
#define EXPORT_C extern "C" EXPORT_CPP
#else
// If a C-compiler uses this header, remove 'extern "C"'
#define EXPORT_C EXPORT_CPP
#endif
/** The macro __cplusplus is used for allowing this
* header to be used from 'C'. If a C compiler is used
* all definitions inside this #ifdef are discarded.
*/
#ifdef __cplusplus
namespace Linalg {
EXPORT_CPP
double norm(const std::vector<double>& xs);
EXPORT_CPP std::vector<double>
linTransform(
double a,
double b,
std::vector<double>& xs
);
EXPORT_CPP
std::ostream&
printVector(std::ostream& os, std::vector<double>& xs);
}
#endif
// ======= C-interface for Linalg namespace =========//
/** Handle or opaque pointer for std::vector<double> */
typedef void* hVectorD;
/* ----- C-Wrappers for Linalg namespace ---- */
EXPORT_C
double testlib_vectorD_Linalg_norm(hVectorD hv);
EXPORT_C
void testlib_vectorD_Linalg_printVector(const char* name, hVectorD hv);
// ======= Non-polymorphic class exported by DLL =========//
#ifdef __cplusplus
// Non-polymorphic class
class EXPORT_CPP SampleClass{
public:
SampleClass();
SampleClass(const std::string& name);
~SampleClass();
std::string getName() const;
int get();
void set(int n);
private:
std::string m_name;
int m_counter;
};
#endif
/* ----- C-Wrappers for SampleClass namespace ---- */
using hSampleClass = void*;
/** Nullable constructor zero-arg constructor */
EXPORT_C
hSampleClass
testlib_SampleClass_make0();
/** Other constructor */
EXPORT_C hSampleClass testlib_SampleClass_make1(const char* name);
/** Destructor */
EXPORT_C
void
testlib_SampleClass_delete(hSampleClass hnd);
/** Wrapper for get method */
EXPORT_C
int
testlib_SampleClass_get(hSampleClass hnd);
/** Wrapper for set method */
EXPORT_C
void
testlib_SampleClass_set(hSampleClass hnd, int n);
EXPORT_C
const char*
testlib_SampleClass_getName(hSampleClass hnd);
//========== Interface class ============//
// Polymorphic Interface class
// binary compatible across different compilers
// as it does not use any STL container on the
// interface.
#ifdef __cplusplus
struct InterfaceClass{
/* Returns class unique ID */
virtual const char* getID() const = 0;
/** Set class internal state */
virtual void setName(const char* name) = 0;
virtual const char* getName() = 0;
/** Virtual constructor */
virtual ~InterfaceClass() = default;
// virtual ~InterfaceClass();
};
#else
#define InterfaceClass void
#endif
/** Factory function */
EXPORT_C InterfaceClass* teslib_InterfaceClass_factory(const char* class_id);
/** C-wrapper for destructor */
EXPORT_C void testlib_InterfaceClass_delete(InterfaceClass* hinst);
/** C-wrapper for getID method */
EXPORT_C const char* testlib_InterfaceClass_getID(InterfaceClass* hinst);
EXPORT_C void testlib_InterfaceClass_setName(InterfaceClass* hinst, const char* name);
EXPORT_C const char* testlib_InterfaceClass_getName(InterfaceClass* hinst);
#endif /** --- End of file */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment