I am trying to create a program that uses plugins in C++ to get some experience with importing and exporting functions from .dll and .so libraries. For simplicity's sake let's only use .dll libraries here.
What I'm trying to do is to make the communication between the plugin and the main program that loaded it "two-way", meaning the main program can call functions from the plugin (this is solved) and the plugin should be able to call functions from the main program (this I'm having trouble with).
I currently am able to create a .dll where I exported the functions with extern "C" {} and __declspec(export).
TestPlugin.h
#pragma once
extern "C" {
__declspec(dllexport) const char* pluginName();
__declspec(dllexport) void onLoad();
__declspec(dllexport) void onShutdown();
}
TestPlugin.cpp
#include "TestPlugin.h"
#include <iostream>
const char * pluginName()
{
return "Test Plugin";
}
void onLoad()
{
std::cout << "onLoad() called!" << std::endl;
}
void onShutdown()
{
std::cout << "onShutdown() called!" << std::endl;
}
I am then loading this test plugin with the following (shortened) code. I removed the error checking and console output.
Plugin.h
#pragma once
#include <filesystem>
#include <iostream>
#include <windows.h>
class Plugin
{
private:
typedef const char*(*pluginNameType)();
typedef void(*onLoadType)();
typedef void(*onShutdownType)();
HINSTANCE m_lib;
public:
Plugin(std::filesystem::path filename);
~Plugin();
pluginNameType pluginName;
onLoadType onLoad;
onShutdownType onShutdown;
};
Plugin.cpp
#include "Plugin.h"
Plugin::Plugin(std::filesystem::path filename)
{
m_lib = LoadLibrary(filename.wstring().c_str());
pluginName = (pluginNameType)GetProcAddress(m_lib, "pluginName");
onLoad = (onLoadType)GetProcAddress(m_lib, "onLoad");
onShutdown = (onShutdownType)GetProcAddress(m_lib, "onShutdown");
}
Plugin::~Plugin()
{
FreeLibrary(m_lib);
}
What I can do now is to call the functions in the plugin (TestPlugin.cpp) from my main program.
main.cpp
Plugin *plugin = new Plugin("pathToDLLGoesHere");
plugin->onLoad();
plugin->onShutdown();
What I would like to do now is to also enable the test plugin I just loaded to have access to functions that are defined in the main program. So let's say in my main.cpp I have something like this ...
main.cpp
int testCall(int val) {
return val + 1;
}
int main()
{
...
return 0;
}
... how would I be able to call the testCall() from the test plugin?
Would it be as simple as to send the function pointer to the plugin and use it? Or do I need to take a different approach here? Thank you for your help!
I have figured out how this works. You can also use extern "C" {} and __declspec(dllexport) to export functions from your main program so the DLLs can see them and when you get the handle of your main program in the DLL, the functions can be called.
In one of your headers in your main program you export the function.
main.h
extern "C" {
__declspec(dllexport) int testCall(int val);
}
main.cpp
int testCall(int val) {
return val + 1;
}
In my test plugin header I created a handle for the main program and a definition for the function I am trying to call from main.
TestPlugin.h
#pragma once
#include <windows.h>
HINSTANCE app;
int(*testCall)(int val);
...
In the body I then assign the handle (calling GetModuleHandle with a nullptr will give you the handle of your program) and then get the exported function from my main program.
TestPlugin.cpp
app = GetModuleHandle(nullptr);
testCall = (int(*)(int val))GetProcAddress(app, "testCall");
After that, I can just call the function.
std::cout << testCall(5) << std::endl;
Related
I often see __declspec(dllexport) / __declspec(dllimport) instructions on Windows, and __attribute__((visibility("default"))) on Linux with functions, but I don't know why. Could you explain to me, why do I need to use theses instructions for shared libraries?
The Windows-exclusive __declspec(dllexport) is used when you need to call a function from a Dll (by exporting it) , that can be accessed from an application.
Example This is a dll called "fun.dll" :
// Dll.h :
#include <windows.h>
extern "C" {
__declspec(dllexport) int fun(int a); // Function "fun" is the function that will be exported
}
// Dll.cpp :
#include "Dll.h"
int fun(int a){
return a + 1;
}
You can now access the "fun" from "fun.dll" from any application :
#include <windows.h>
typedef int (fun)(int a); // Defining function pointer type
int call_fun(int a){
int result = 0;
HMODULE fundll = LoadLibrary("fun.dll"); // Calling into the dll
if(fundll){
fun* call_fun = (fun*) GetProcAddress(fundll, "fun"); // Getting exported function
if(call_fun){
result = call_fun(a); // Calling the exported fun with function pointer
}
}
return result;
}
I am working with Unity and I need to pass callback delegate from C# unity script to .dll
There is a api (MyApi.h) that I have on .dll side
typedef void(__stdcall * FuncPtr) (const char * str);
static FuncPtr DebugLog = nullptr;
static void debug_in_unity(std::string message)
{
if (DebugLog)
{
DebugLog(message.c_str());
}
}
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback)
{
if (callback)
{
DebugLog = callback;
}
}
...
}
so, when I call register_debug_callback function from C# side I see that everything is ok and DebugLog assigned as expected.
Then in order to send my log message from .dll side to C# I need to call this function debug_in_unity()
So, I have another myfile.cpp file where I need to use this log function
#include "MyApi.h"
void MyClass::foo()
{
std::string log = "HERE!!!";
debug_in_unity(log);
}
So, everything looks fine, I have a global static method debug_in_unity and global func DebugLog that I assigned previously here register_debug_callback
But what actually is happen is - when I call this method register_debug_callback I see that I assigned DebugLog variable, but then when I call this method debug_in_unity I see that DebugLog is null. Looks like static variable is kind of not global like MyApi.h has instance of DebugLog and myfile.cpp has his own instance. I assume that it is a reason why I see assignment and then I see that the same value is null...
But how to use it properly? How to fix it?
You are breaking the one definition rule. You will need to have one translation unit (.c or .cpp file) defining DebugLog, and the header declaring it extern. Similarly your functions are also defined in multiple translation units.
MyApi.h
typedef void(__stdcall * FuncPtr) (const char * str);
extern FuncPtr DebugLog;
void debug_in_unity(std::string message);
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback);
// ...
}
MyApi.cpp
#include "MyApi.h"
FuncPtr DebugLog = nullptr;
void debug_in_unity(std::string message)
{
if (DebugLog)
{
DebugLog(message.c_str());
}
}
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback)
{
if (callback)
{
DebugLog = callback;
}
}
// ...
}
I'm doing functional testing on c code. Have include .c file in test .cpp file so far, everything works well. But I need to include the same .c file in another .cpp file where I do other unit tests. Then I get problems that is already defined. Because I already include it in the first file cpp.
If merge all the test methods into one cpp file then it works well. If keep all unit test in one file get to big handle so I need to split up diffrent files in same project it also create help functions for secure that functions get in capsules.
This not a normal LNK2005 because I can not declare the variable and functions in .h: as extern BOOL MyBool; and then assign to it in a .c or .cpp file. Because need include c file as I do unit test this function. Also I can't or should avoid do any changes .c.
I looking way to keep include of .c local not effect other files in same project.
source.h
#ifndef SOURCE_H_
#define SOURCE_H_
#include "../car.h"
enum INITIALMODE {
INITIALMODE_NOT_POSITIONING, // 0
INITIALMODE_NO_DRIVER_INPUT, // 1
INITIALMODE_POSITION_LOW_POSITION, // 2
INITIALMODE_POSITION_STANDARD_POSITION, // 3
INITIALMODE_POSITION_HIGH_POSITION // 4
};
void initMotor(void);
#endif
source.c
/* Compiler include files */
#pragma once
#include "positioning.h"
#include "api.h"
#include "drive.h"
#include "types.h"
static void updateTarget(void);
static SWord getMax(UWord Limit, UWord Aux);
static DWord getHeight(void);
static Bool isMode(void);
static Bool isExiting(void);
#define cMaxHeight 100 * Profile.s.Max /* m -> mm */
void initMotor(void)
{
// do something
}
static void updatePositioning(void)
{
// do something
}
Test files look like this, however, this is very scaled for making exemple small.
UnitTest.cpp and UnitTest2.cpp
#include "CppUnitTest.h"
#ifndef UNIT_TEST
#define UNIT_TEST
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace Test
{
extern "C" {
#include "../../Test/source.h"
#include "../../Test/source.c"
}
TEST_CLASS(UnitTest_1)
{
public:
TEST_METHOD(Test_1)
{
// Verify that the initial state is as expected.
initTest();
//Expected value
UWord Expected = 500
//Trigger to execute.
UWord Test = updatePositioning();
// Verify that the results are as expected.
Assert::AreEqual(Expected, Test);
}
};
}
#endif
You should never include a .C or .CPP file.
However, you can compile C code in C++. Here's an example based on the information you gave in your initial post.
YourCCode.h
#ifndef YourCCode_H
#define YourCCode_H
int FunctionToTest(int SomeParams);
int TestStaticFunctions(int SomeParam1, int SomeParam2);
#endif // YourCCode_H
YourCCode.c
#include "YourCCode.h"
static int SomeStaticFunction(int Param1, int Param2)
{
return Param1 + Param2; // that needs extensive testing, obviously.
}
int FunctionToTest(int SomeParams)
{
return SomeStaticFunction(SomeParams, 1);
}
int TestStaticFunctions(int SomeParam1, int SomeParam2)
{
return SomeStaticFunction(SomeParam1, SomeParam2);
}
UnitTest1.cpp
extern "C" {
#include "YourCCode.h"
}
bool TestFunction(int Value)
{
return (FunctionToTest(Value) == Value+1);
}
UnitTest2.cpp
extern "C" {
#include "YourCCode.h"
}
void AnotherTestFunction(int Val, int Val2)
{
int newValue = TestStaticFunctions(Val, Val2);
ASSERT(newValue == Val+Val2);
}
Then compile your CPP and C file.
After you clarified your intent, I realized you're trying to test static functions from another unit. Static function, by definition, are only available to other functions in the same translation unit. This is mostly use as a protection to prevent programmers to call some functions without knowing how to validate their inputs, know the call order, etc...
My best bet here is either you choose that your functions are not static and you can test them from outside your translation unit, or you implement the test functions inside the translation unit containing those static functions. I would recommend the latter as a translation unit should know (architecturally speaking) how to test their own features.
As a third solution, if you don't have any control over the content of the C file (but since you have that C file, I doubt it), you could have a proxy CPP file that includes the C file, and create a proxy call for each static call.
That is a ugly hack, though, and could break very easily if the C file ever gets updated, so I'm advising against it.
Here's a quick example :
YourCCode.h
#ifndef YourCCode_H
#define YourCCode_H
void SomeFunction(void);
#endif // YourCCode_H
YourCCode.c
#include "YourCCode.h"
static int AddSomething(int Param1, int Param2)
{
return Param1 + Param2;
}
static int SubtractSomething(int Param1, int Param2)
{
return Param1 - Param2;
}
void SomeFunction(void)
{
// code meant to be called externally.
}
ProxyTestCode.hpp
bool TestAddSomething(void);
bool TestSubtractSomething(void);
ProxyTestCode.cpp
extern "C" {
#include "YourCCode.h"
#include "YourCCode.c"
}
bool TestAddSomething(void)
{
return (AddSomething(2,2) == 4);
}
bool TestSubtractSomething(void)
{
return (AddSomething(2,2) == 0);
}
UnitTest1.cpp
#include "ProxyTestCode.hpp"
void TestAdd(void)
{
ASSERT(TestAddSomething());
}
UnitTest2.cpp
#include "ProxyTestCode.hpp"
void TestSubtract(void)
{
ASSERT(TestSubtractSomething());
}
If you do that, don't compile your C file in your project.
I am attempting to program an Atmel SAM D21 microcontroller using C++ in Atmel Studio. I'm trying to create periodic hardware interrupts using one of the on-chip timers.
I created Timer4 class to set up the timer from main.cpp. I tried to create a Timer4 instance called MyTimer4 in the main function, but it says
'Timer4' was not declared in this scope
'MyTimer4' was not declared in this scope
I've seen many similar discussions pointing to incorrect/circular #includes. But, I don't seem to see the same problem on my own. Any ideas?
Main.cpp
#include "timerSAMD21.h"
#include "sam.h"
void SampleADC(void)
{
}
int main(void)
{
SystemInit();
Timer4 MyTimer4;
MyTimer4.setRate(1000);
MyTimer4.onEvent(SampleADC);
MyTimer4.start;
}
timerSAMD21.h
#ifdef TIMERSAMD21_H
#define TIMERSAMD21_H
#include "tc.h"
#include "tc4.h"
#include "gclk.h"
typedef void (*voidFuncPtr)(void);
class Timer4
{
public:
Timer4() {};
void setRate(int frequency);
void start(void);
void end(void);
void onEvent(voidFuncPtr funcOnEvent);
private:
void configure(int frequency);
void enable(void);
void disable(void);
void reset(void);
};
#endif
timerSAMD21.cpp
#include "timerSAMD21.h"
voidFuncPtr callback = NULL;
void Timer4::setRate(int frequency) {
configure(frequency);
}
void Timer4::start(void) {
enable();
}
void Timer4::end(void) {
disable();
reset();
}
void Timer4::configure(int frequency) {
//Configuration code here. Removed for Stack Overflow.
}
void Timer4::enable(void){
REG_TC4_CTRLA |= TC_CTRLA_ENABLE; //Enable timer
while (TC4->COUNT8.STATUS.bit.SYNCBUSY);
}
void Timer4::disable(void) {
REG_TC4_CTRLA &= ~TC_CTRLA_ENABLE;
while (TC4->COUNT8.STATUS.bit.SYNCBUSY);
}
void Timer4::reset(void) {
REG_TC4_CTRLA = TC_CTRLA_SWRST;
while (TC4->COUNT8.STATUS.bit.SYNCBUSY);
while (TC4->COUNT8.CTRLA.bit.SWRST);
}
void Timer4::onEvent(voidFuncPtr funcOnEvent){
callback = funcOnEvent;
}
#ifdef __cplusplus
extern "C" {
#endif
void IRQHandlerTimer4(void) {
if (callback != NULL)
{
callback();
}
REG_TC4_INTFLAG = TC_INTFLAG_MC0;
}
#ifdef __cplusplus
}
#endif
(Note: Making an answer in order to get this out of the list of unanswered questions. Miles seems to have decided not to answer and I do not consider the problem a typo.)
The way you attempt to prevent the reinclusion of your header is causing it to only make the content of the header visible if the guard-macro happens to be defined already, which it of course never is.
In order to fix this, change the
#ifdef TIMERSAMD21_H
#define TIMERSAMD21_H
into
#ifndef TIMERSAMD21_H
#define TIMERSAMD21_H
This will first keep the header content visible the first time it is included.
It will then define the guard macro, which will prevent the header content from being compiled a second time in the same translation unit, i.e. code file.
I have this pieces of code:
class DLL_API MyClassWrapper
{
private:
MyClass * m_myClass;
public:
MyClassWrapper(SIZE inputSize);
~MyClassWrapper();
inline int OutputSize();
}
typedef std::shared_ptr<MyClassWrapper> MyClassWrapperPtr;
extern "C"
{
DLL_API MyClassWrapperPtr CreatreMyClassWrapper(SIZE inputSize)
{
return std::make_shared<MyClassWrapper>(inputSize);
}
}
But it doesn't work, with error:
Error 1 error C2526: CreatreMyClassWrapper: C linkage function cannot return C++ class 'std::shared_ptr<_Ty>'
I understand the problem, but how can I fix it?
The options that I can see are:
1- Don't pass a shared pointer. which means that DLL user should delete the pointer after they used it.
2- Don't use extern "C" : which means that I must use mangled names.
Is there any other solution?
Straight to the point, to return C++ object from C function - just returns it via output arguments:
extern "C"
{
DLL_API void CreatreMyClassWrapper(SIZE inputSize, SomeClass* outputPtr)
{
*outputPtr = SomeClass(....);
}
}
In your example SomeClass == MyClassWrapperPtr, so:
extern "C"
{
DLL_API void CreatreMyClassWrapper(SIZE inputSize, MyClassWrapperPtr* outputPtr)
{
*outputPtr = make_shared<MyClassWrapper>(inputSize);
}
}
Consider however to change your interface a little, because in current shape you need to be sure that your applications and DLLs shall use the same compiler, linker, settings, libraries...*
You might want to export Create and Delete from your DLL to be sure memory management will occur in your DLL (this is based on this answer:
DLL
extern "C"
{
DLL_API MyClassWrapper* CreateMyClassWrapper(SIZE inputSize)
{
return new MyClassWrapper(inputSize);
}
DLL_API void DeleteMyClassWrapper(MyClassWrapper* wrapper)
{
delete wrapper;
}
}
Application
shared_ptr<MyClassWrapper> myClassWrapper(CreateMyClassWrapper(inputSize),
DeleteMyClassWrapper);