For example I have name1.dll
with methods:
void func1(int)
int func2(bool, char)
and name2.dll
with methods:
std::string func1()
bool func2(int, int, int)
To work with them, I want to get some generated classes and their objects, respectively, using the following syntax
DECLARE_WRAPPER(
Wrapper1,
DECLARE_METHOD(func1, void(int))
DECLARE_METHOD(func2, int(bool, char))
)
And
DECLARE_WRAPPER(
Wrapper2,
DECLARE_METHOD(func1, std::string())
DECLARE_METHOD(func2, bool(int, int, int))
)
and use like this:
Wrapper1 w1("name1.dll");
w1 func1(6);
w1.func2(false, 'c');
Wrapper w2("name2.dll");
w2.func1();
w2.func2(6, 5, 7)
My C++ code so far for this purpose following:
#include <iostream>
#include <string>
#include <windows.h>
#define DECLARE_METHOD(name, ret, ...) \
ret name(__VA_ARGS__) { \
FARPROC proc = GetProcAddress(m_hDll, #name); \
if (proc == nullptr) { \
throw std::runtime_error("Function not found"); \
} \
va_list args; \
va_start(args, __VA_ARGS__); \
ret result = reinterpret_cast<ret(__cdecl*)(__VA_ARGS__)>(proc)(__VA_ARGS__); \
va_end(args); \
return result; \
}
#define DECLARE_WRAPPER(wrapperName, ...) \
class wrapperName { \
public: \
wrapperName(const std::string& dllPath) : m_hDll(nullptr) { \
m_hDll = LoadLibraryA(dllPath.c_str()); \
} \
~wrapperName() { \
if (m_hDll) { \
FreeLibrary(m_hDll); \
} \
} \
__VA_ARGS__ \
private: \
HMODULE m_hDll; \
};
DECLARE_WRAPPER(
Wrapper1,
DECLARE_METHOD(func1, void, int)
DECLARE_METHOD(func2, int, bool, char)
);
DECLARE_WRAPPER(
Wrapper2,
DECLARE_METHOD(func1, std::string)
DECLARE_METHOD(func2, bool, int, int, int)
);
int main() {
const std::string dllPath1 = "name1.dll";
const std::string dllPath2 = "name2.dll";
Wrapper1 w1(dllPath1);
w1.func1(6);
int res = w1.func2(false, 'c');
std::cout << "Wrapper1::func2 returned " << res << std::endl;
Wrapper2 w2(dllPath2);
std::string str = w2.func1();
std::cout << "Wrapper2::func1 returned " << str << std::endl;
bool b = w2.func2(6, 5, 7);
std::cout << "Wrapper2::func2 returned " << b << std::endl;
return 0;
}
But I have errors, and I can't make it work. How can this code be fixed? Or, am I trying to achieve something that can't be done in C++? Current version I can use is C++17
.
I don't think you can use DECLARE_METHOD()
inside of DECLARE_WRAPPER()
the way you are doing. You will likely have to break up DECLARE_WRAPPER()
into separate macros so you can then use DECLARE_METHOD()
s in between them.
However, DECLARE_METHOD()
itself will certainly not work the way you have written it. It canont use __VA_ARGS__
to both declare the method parameters and to call the loaded proc()
. And, it cannot pass __VA_ARGS__
to va_start()
at all (not that it matters since you are not using args
anyway). You should use C++-style variadic templates instead of C-style variadic macros.
Try something more like this:
#define DECLARE_METHOD(name, returnType) \
template<typename... Args> \
returnType name(Args&&... args) \
{ \
using procType = returnType (__cdecl *)(Args...); \
procType proc = reinterpret_cast<procType>(GetProcAddress(m_hDll, #name)); \
if (proc == nullptr) { \
throw std::runtime_error("Function not found"); \
} \
return proc(std::forward<Args>(args)...); \
}
#define DECLARE_WRAPPER_BEGIN(wrapperName) \
class wrapperName { \
public: \
wrapperName(const std::string& dllPath) : m_hDll(nullptr) { \
m_hDll = LoadLibraryA(dllPath.c_str()); \
if (m_hDll == nullptr) \
throw std::runtime_error("DLL not found"); \
} \
~wrapperName() { \
FreeLibrary(m_hDll); \
}
#define DECLARE_WRAPPER_END() \
private: \
HMODULE m_hDll; \
};
DECLARE_WRAPPER_BEGIN(Wrapper1)
DECLARE_METHOD(func1, void)
DECLARE_METHOD(func2, int)
DECLARE_WRAPPER_END()
DECLARE_WRAPPER_BEGIN(Wrapper2)
DECLARE_METHOD(func1, std::string)
DECLARE_METHOD(func2, bool)
DECLARE_WRAPPER_END()
On a side note, it is not safe for Wrapper2::func1()
to pass a std::string
across the DLL boundary. If the DLL function returns a char*
that you want converted into a std::string
, then your DECLARE_METHOD()
macro will have to account for that, by having separate parameters to specify the two return types individually. For example:
#define DECLARE_METHOD(name, dllReturnType, methReturnType) \
template<typename... Args> \
methReturnType name(Args&&... args) \
{ \
using procType = dllReturnType (__cdecl *)(Args...); \
procType proc = reinterpret_cast<procType>(GetProcAddress(m_hDll, #name)); \
if (proc == nullptr) { \
throw std::runtime_error("Function not found"); \
} \
return proc(std::forward<Args>(args)...); \
}
}
DECLARE_WRAPPER_BEGIN(Wrapper1)
DECLARE_METHOD(func1, void, void)
DECLARE_METHOD(func2, int, int)
DECLARE_WRAPPER_END()
DECLARE_WRAPPER_BEGIN(Wrapper2)
DECLARE_METHOD(func1, char*, std::string)
DECLARE_METHOD(func2, bool, bool)
DECLARE_WRAPPER_END()