This question is about C++ <-> C++ interoperability.
As is well known implementation of standard library classes/functions may differ across different vendors. Moreover implementation may differ even within same library vendor, when using different compiler keys, configuration (Debug/Release), etc.
Due to that reason, many library developers shifts to old plain C-style API.
Which leads to uglish error-prone interfaces.
For instance, in order to get string from some function, interfaces like Win GetCurrentDirectory function are used:
DWORD WINAPI GetCurrentDirectory(
__in DWORD nBufferLength,
__out LPTSTR lpBuffer
);
three parameters + some boilerplate code on both sides(checking if buffer size was enough, etc) just to get simple string.
I am thinking to use some auxiliary adapter/proxy class, which will do all conversions automaticly, and can be simply reused.
Something like:
#include <string>
#include <algorithm>
#include <iostream>
#include <ostream>
class StringConverter
{
char *str; // TODO: use smart pointer with right deleter
public:
StringConverter(const std::string &user_string) // Will be defined only at user side
{
str=new char[user_string.length()+1];
(*(std::copy(user_string.begin(),user_string.end(),str)))=0;
}
operator std::string() // Will be defined only at library side
{
return std::string(str);
}
~StringConverter()
{
delete [] str;
}
};
StringConverter foo()
{
return std::string("asd");
}
int main(int argc,char *argv[])
{
std::cout << std::string(foo()) << std::endl;
return 0;
}
http://ideone.com/EfcKv
Note, I plan to have defenition of conversion from user string to StringConverter only at user side, and defenition of conversion from StringConverter to library string only inside library.
Also, right deleter should be used (from right heap).
What do you think about such approach?
Are there some major pitfalls?
Are there some superior alternatives?
This technique will work in some cases where standard data types are incompatible, but in others it will fare no better: name mangling differences and class memory layout differences (vptrs and tags) come to mind.
This is why C APIs are preferred.
But you can improve usability by burying the C API where the library caller never needs to see it. Then add a thin, idiomatic C++ overlay that provides the visible library interface. In some cases the thin overlay code can be used on both caller and library side, each compiled in its own environment: flags, link conventions, etc. Only C data types are exchanged. The simpler these data types are, the more compatibility you'll obtain.
The layer also takes care that memory allocation and deallocation occur on the same side of the API on a per object basis, as your code does. But it can be more flexible. For example, it's possible to arrange for an object allocated in the caller to be deallocated in the library and vice versa.
// library.h for both caller and library
// "shadow" C API should not be used by caller
extern "C" {
void *make_message(char *text);
char *get_text_of_message(void* msg);
void send_message(void *msg); // destroys after send.
}
// Thin C++ overlay
class Message {
void *msg;
public:
Message(const std::string &text) {
msg = make_message(text.c_str());
}
void send() {
if (msg) send_message(msg);
else error("already sent");
msg = 0;
}
std:string getTextString() {
return std:string(get_text_of_message(void* msg));
}
}
Related
I'm trying to make some sort of C++ "bridge" to connect an unmanaged C++ dll on one end (without modifying their code) to a C# Wrapper which uses DllImport for various imports.
I was able to pass a C# string to my bridge using char pointers, but the receiving Dll needs to receive std::string, so I tried with std::string(foo); with no luck, it always gets transformed into weird characters.
The structure is the following :
C# Wrapper
[DllImport(#"Bridge.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void initDetector(string foo, int something = 0);
C++ Bridge
extern "C" __declspec(dllexport) void initCppClass(char* foo, int something)
{
std::string bar = std::string(foo);
std::cout << bar << std::endl; //Returns "foo"
instance = new CppClass(bar, something);
}
C++ Imported DLL (not allowed to change code here)
CppClass::CppClass(std::string foo, int something)
{
std::cout << foo << std::endl; //Returns garbage
}
Note that this constructor is for demonstration purposes only, as I cannot disclose the original code.
I originally tried passing the char* directly to the constructor but that didn't work either. Is there something I'm missing here ?
I think the problem is different string encoding.
Try adding CharSet = CharSet.Ansi in C#, like this:
[DllImport(#"Bridge.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
However, please read the documentation of that C++ dll API. A lot of C++ code, especially if that’s cross-platform code, expect UTF8-encoded strings. If that’s your case, instead change the bridge to
extern "C" __declspec(dllexport) void initCppClass(const wchar_t* foo, int something)
And write code to convert the string from UTF16-encoded C pointer into UTF8-encoded std::string, see this answer for an example.
Update: another possible reason is different STL, or different CRT. When you pass std::string or any other STL objects objects across DLL boundaries, you have to use same compiler & same version of it, same build settings for (e.g. in VC++, std::strings memory layout differs between debug and release builds), and also both DLLs must link to CRT dynamically., e.g. Multi-threaded DLL (/MD)
I need to register functions like the following in a list of functions with arguments.
void func1( int a , char* b ) {}
void func2( vec3f a , std::vector<float> b , double c) {}
...
And call them back when I receive data over network with proper arguments. I imagined va_list would solve, but it doesnt work :
void func1(int a, char* b)
{
printf("%d %s",a,b);
}
void prepare(...)
{
va_list argList;
int args = 2;
va_start(argList, args);
((void (*)(va_list))func1)(argList);
va_end(argList);
}
int main(int argc, char **argv)
{
prepare(1, "huhu");
return 0;
}
What is the most elegant way to solve this ?
I know std::bind / std::function has similar abilities, but the internal data is hidden deep in std I assume. I just need a few basic data types, doesnt have to be for arbitrary types. If preprocessor tricks with ##VA_ARGS or using templates would solve, I am also OK with that. Priority is that it is most simple to use.
Edit1 : I found that assembly can solve ( How do I pass arguments to C++ functions when I call them from inline assembly ) - but I would prefer a more platform independent solution.
If your goal is to create your own, small and ad-hoc "rpc" solution, possibly one of the major drivers for making decisions should be: 1. Minimal amount of code 2. Easy as possible.
Keeping that in mind, it is paying off to ponder, what the difference is between the following 2 scenarios:
"Real" RPC: The handlers shall be as you wrote with rpc-method-specific signature.
"Message passing": The handlers receive messages of either "end point-determined type" or simply of a unified message type.
Now, what has to be done to get a solution of type 1?
Incoming byte streams/network packets need to get parsed to some sort of message with regards to some chosen protocol. Then, using some meta-info (contract), according to { serviceContract, serviceMethod }, a specific set of data items needs to be confirmed in the packet and if present, the respective, registered handler function needs to be called. Somewhere within that infrastructure you typically have a (likely code generated) function which does something like that:
void CallHandlerForRpcXYCallFoo( const RpcMessage*message )
{
uint32_t arg0 = message->getAsUint32(0);
// ...
float argN = message->getAsFloat(N);
Foo( arg0, arg1, ... argN );
}
All that can, of course also be packed into classes and virtual methods with the classes being generated from the service contract meta data. Maybe, there is also a way by means of some excessive template voodoo to avoid generating code and having a more generic meta-implementation. But, all that is work, real work. Way too much work to do it just for fun. Instead of doing that, it would be easier to use one of the dozens technologies which do that already.
Worth noting so far is: Somewhere within that piece of art, there is likely a (code generated) function which looks like the one given above.
Now, what has to be done to get a solution of type 2?
Less than for case 1. Why? Because you simply stop your implementation at calling those handler methods, which all take the RpcMessage as their single argument. As such, you can get away without generating the "make-it-look-like-a-function-call" layer above those methods.
Not only is it less work, it is also more robust in the presence of some scenarios where the contract changes. If one more data item is being added to the "rpc solution", the signature of the "rpc function" MUST change. Code re-generated, application code adapted. And that, whether or not the application needs that new data item. On the other hand, in approach 2, there are no breaking changes in the code. Of course, depending on your choices and the kind of changes in the contract, it still would break.
So, the most elegant solution is: Don't do RPC, do message passing. Preferably in a REST-ful way.
Also, if you prefer a "unified" rpc message over a number of rpc-contract specific message types, you remove another reason for code bloat.
Just in case, what I say seems a bit too abstract, here some mock-up dummy code, sketching solution 2:
#include <cstdio>
#include <cstdint>
#include <map>
#include <vector>
#include <deque>
#include <functional>
// "rpc" infrastructure (could be an API for a dll or a lib or so:
// Just one way to do it. Somehow, your various data types need
// to be handled/represented.
class RpcVariant
{
public:
enum class VariantType
{
RVT_EMPTY,
RVT_UINT,
RVT_SINT,
RVT_FLOAT32,
RVT_BYTES
};
private:
VariantType m_type;
uint64_t m_uintValue;
int64_t m_intValue;
float m_floatValue;
std::vector<uint8_t> m_bytesValue;
explicit RpcVariant(VariantType type)
: m_type(type)
{
}
public:
static RpcVariant MakeEmpty()
{
RpcVariant result(VariantType::RVT_EMPTY);
return result;
}
static RpcVariant MakeUint(uint64_t value)
{
RpcVariant result(VariantType::RVT_UINT);
result.m_uintValue = value;
return result;
}
// ... More make-functions
uint64_t AsUint() const
{
// TODO: check if correct type...
return m_uintValue;
}
// ... More AsXXX() functions
// ... Some ToWire()/FromWire() functions...
};
typedef std::map<uint32_t, RpcVariant> RpcMessage_t;
typedef std::function<void(const RpcMessage_t *)> RpcHandler_t;
void RpcInit();
void RpcUninit();
// application writes handlers and registers them with the infrastructure.
// rpc_context_id can be anything opportune - chose uint32_t, here.
// could as well be a string or a pair of values (service,method) or whatever.
void RpcRegisterHandler(uint32_t rpc_context_id, RpcHandler_t handler);
// Then according to taste/style preferences some receive function which uses the registered information and dispatches to the handlers...
void RpcReceive();
void RpcBeginReceive();
void RpcEndReceive();
// maybe some sending, too...
void RpcSend(uint32_t rpc_context_id, const RpcMessage_t * message);
int main(int argc, const char * argv[])
{
RpcInit();
RpcRegisterHandler(42, [](const RpcMessage_t *message) { puts("message type 42 received."); });
RpcRegisterHandler(43, [](const RpcMessage_t *message) { puts("message type 43 received."); });
while (true)
{
RpcReceive();
}
RpcUninit();
return 0;
}
And if RpcMessage then is traded, while packed in a std::shared_ptr, you can even have multiple handlers or do some forwarding (to other threads) of the same message instance. This is one particularly annoying thing, which needs yet another "serializing" in the rpc approach. Here, you simply forward the message.
I am a C++ (MSVC) writer, VB newbie trying to assist an expert VB.net writer who has just not done this task before.
We wish to develop both C/C++ and VB applications to use a DLL written in C++ with C extern-ed API functions. The C++ program is working just fine. It's VB where we are having difficulties.
The DLL provides an extern C function:
RegisterCallback( void* cbFuncPtr, void* dataPtr );
NOTE 1: See my note below for a design change and the reasons we made it.
NOTE 2: Additional update added as an answer below.
where the callback function havs this C typedef:
typedef (void)(* CALL_NACK)(void*);
The cbFuncPtr is expected to be a function pointer to some VB function that will get called as the CALL_BACK. The dataPtr is a pointer to a data structure that has this C definition:
typedef struct
{
int retCode;
void* a_C_ptr;
char message[500];
} cbResponse_t;
where a_C_ptr is an internal pointer in the DLL that the VB can cast tolong`. It uniquely identifies where in the DLL the callback was made and allows the VB function to recognize calls from same/different locations.
We are able to access and run the RegisterCallback() function from VB just fine. Logging shows we get there and that data is passed in. It is the actual data that seems to be the problem.
In reading about a million forum entries we have learned that VB doesn't know what pointers are and that a VB structure is more than just organized memory. We're pretty sure the "address" of a VB structure is not what C thinks an address is. We've seen repeated references to "marshaling" and "managed data", but lack enough understanding to know what that is telling us.
How should we code VB to give the DLL the execution address of its callback function and how do we code up a VB construct that the DLL can fill in just as it does for C++?
Might we need a DLL function where the calling app can say "C" or "VB" andhave the DLL handle the sturcture pointers differently? If so, how would one code up C to fill in the VB structure?
This is a bit too big and deep to just be an edit to the original posting...
From the link posted by #DaveNewman, I extracted this gem:
Here's a bit about the compact framework, but it's the same in the
grown-ups framework:
The .NET Compact Framework supports automatic marshaling of structures
and classes that contain simple types. All fields are laid out
sequentially in memory in the same order as they appear in the
structure or class definition. Both classes and structures appear in
native code as pointers to C/C++ structs.
Objects in the managed heap can be moved around in memory at any time
by the garbage collector, so their physical addresses may change
without notice. P/Invoke automatically pins down managed objects
passed by reference for the duration of each method call. This means
pointers passed to unmanaged code will be valid for that one call.
Bear in mind that there is no guarantee that the object will not be
moved to a different memory address on subsequent calls.
http://msdn.microsoft.com/en-us/library/aa446538.aspx#netcfmarshallingtypes_topic6
This is major hurdle for a RegisterCallback( fcnPtr, dataPtr) function. The pointer passed in at registration time could change at any time the RegisterCallback() is not the current statement. The posting author summed it up this way
You don't need to do anything as the structures are pinned automatically for duration of the call.
implying, of course, not pinned down outside the call.
For this reason we decided on a design change to have the response structure built in, so to speak, the C/C++ world of the DLL, not in VB's space. That way it'll stay put. The actual callback function's signature will remain unchanged so the VB program can know where the DLL put the response. This also allows the responders in the DLL to allocate separate response structures for separate needs.
Once again my update is too large for a mere comment!
Update 18 Apr 2013:
Well, the attempt to use the code from Calling Managed Code from Unmanaged Code cited above was a bust. We ended up having to add /clr to the DLL make turning the DLL into managed code, which made it unusable from a C application.
We are now testing the example at Callback Sample which I was able to show made a DLL that worked with both VB and C++. You'd need to have the PinvokeLib.dll Source to make this work.
Here is the code for the C++ (C really) tester. Compiled as a MSVC project.
NOTE: Notice the __cdecl in this line:
typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i );
It was the secret I had to find. The DLL and this app are compiled with __cdecl linkage, not __stdcall. They are the default in VC++ and I just used the defaults. I tried changing everything to __stdcall but that didn't work. Has to be __cdecl.
// PinvokeTester.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <cstdlib>
#include <string.h>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <Windows.h>
#define PINVOKELIB_API __declspec(dllimport)
HINSTANCE hLib; // Windows DLL handle
bool CALLBACK VBCallBack( int value );
bool AttachLibrary( void );
void * GetFuncAddress( HINSTANCE hLib, const char* procname );
int main(int argc, char* argv[])
{
if ( !AttachLibrary() )
{
printf( "Lib did not attach.\n" );
exit(1);
}
typedef bool (CALLBACK *BOOL_FP_INT)(int i );
typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i );
FPtr TestCallBack = (FPtr)GetFuncAddress( hLib, "TestCallBack" );
TestCallBack( (BOOL_FP_INT)VBCallBack, 255 );
return 0;
}
bool CALLBACK VBCallBack( int value )
{
printf( "\nCallback called with param: %d", value);
return true;
}
bool AttachLibrary( void )
{
// Get a var for the IPC-dll library.
std::string dllName;
/*--- First, link to the IPC-dll library or report failure to do so. ---*/
dllName = ".\\PinvokeLib";
if ( NULL == (hLib = LoadLibraryA( dllName.c_str() )) )
{
printf( "\nERROR: Library \"%s\" Not Found or Failed to Load. \n\n", dllName.c_str() );
printf( "\"%s\"\n", GetLastError() );
return false;
}
return true;
}
//=====================================================================
void * GetFuncAddress( HINSTANCE hLib, const char* procname )
{
void * procAddr = NULL;
procAddr = (void *)GetProcAddress( hLib, procname );
// If the symbol wasn't found, handle error ---------------------
if ( NULL == procAddr )
{
std::cout << "ERROR: Could not get an address for the \""
<< procname << "\" function. : "
<< GetLastError() << std::endl;
exit( 7 );
procAddr = (void*)NULL;
}
return procAddr;
}
As previously discussed here, I'm trying to find a workaround for the LNK2019 issue that arises when building a static library which utilizes C++ templates, and separating the source from the header to keep the code private from other projects. I believe I've nearly come to a working conclusion (for my particular situation), but I'm not entirely sure if this is the correct/best way to go about it and was wondering if anyone has any suggestions, improvements/comments to add?
The goal is to do some type checking to see if the template's signature matches the target prototype function's signature, do some private processing, and return whether or not it was sucessful. NOTE that I have removed SdkHookMgr.h and SdkHookMgr.cpp from the prior version of the solution in the above link, and merged everything back into SdkLib.h and SdkLib.cpp, into a static class for a bit of clarity.
SdkLib.h:
#include <typeinfo>
#ifdef MY_EXPORTS
# define MYDECL __declspec(dllexport)
#else
# define MYDECL
#endif
// Prototypes
typedef HMODULE (WINAPI *HookLoadLibraryA)( LPCSTR lpFileName );
//...
class CHook;
class CHookManager;
MYDECL BOOL WINAPI ValidateHook( CHook *hook );
class CHook
{
public:
CHook() : m_type(NULL), m_target(NULL), m_result(FALSE) {};
CHook( const char *type, PVOID target ) : m_type(type), m_target(target) {
m_result = ValidateHook(this);
};
const char *m_type;
PVOID m_target;
BOOL m_result;
};
class CHookManager
{
public:
template <typename HookFunction> static BOOL Hook(HookFunction target)
{
const type_info& type = typeid(HookFunction);
CHook *hook = new CHook( type.name(), target );
return hook->m_result;
}
};
SdkLib.cpp:
#include <SdkLib.h>
IDXDECL BOOL WINAPI ValidateHook( CHook *hook )
{
// Do type checking, private processing, etc here...
return TRUE;
}
DemoDLL.cpp:
#include <SdkLib.h>
HMODULE WINAPI Hooked_LoadLibraryA( LPCSTR lpFileName )
{
DebugBreak();
}
// The function that starts the rollercoaster.
// - Syntax: Hook< prototype >( target )
if!(CHookManager::Hook<HookLoadLibraryA>(Hooked_LoadLibraryA))
cout << "Failed to create hook for LoadLibraryA!" << endl;
You may find that the results of typeid are not consistent between the DLL and the main program. (See, for example, typeid result across different dll's.)
Since your list of possible hooks is limited, it strikes me that overloaded functions would be a better choice than templates. You'd then have no DLL issues, and the validity of each hook would be checked at compile time. Here's an example of the sort of thing I'm thinking of; obviously in practice you'd split this into separate definition and declaration, with the definitions living in the DLL so it's all cleanly separated out.
class CHookManager {
public:
BOOL Hook(HookLoadLibraryA hook) {
assert(sizeof hook<=sizeof(uintptr_t));
return ValidateHook((uintptr_t)hook,"LoadLibraryA");
}
BOOL Hook(HookLoadLibraryW hook) {
assert(sizeof hook<=sizeof(uintptr_t));
return ValidateHook((uintptr_t)hook,"LoadLibraryW");
}
};
(Note that this shows up one disadvantage of this approach - you can only have one hook per function signature. I mention this for completeness' sake, but I'll assume this hasn't proven an issue.)
(You might like to replace the assert with a compile-time assert, if you have one.)
ValidateHook would use strcmp to figure out which hook is being hooked. Once it's figured out which hook it is, it would then cast the uintptr_t to the appropriate function pointer type. It knows the pointer was originally of the correct type for that hook, because you're using the C++ overload mechanism to do it all. (Or you could have an enum, say, for all the hook types, rather than passing in a string - it's up to you. The key part is that you have full control over the values being passed, so that the DLL and the calling code are definitely using matching values.)
This code would be a little tiresome to generate, but if you already have the list of typedef names then you could create the corresponding code using regular expression search and replace, or keyboard macros, in your editor of choice. Or you could use something like the so-called "X-Macro" to automate the generation of the whole thing.
this is my first question after long time checking on this marvelous webpage.
Probably my question is a little silly but I want to know others opinion about this. What is better, to create several specific methods or, on the other hand, only one generic method? Here is an example...
unsigned char *Method1(CommandTypeEnum command, ParamsCommand1Struct *params)
{
if(params == NULL) return NULL;
// Construct a string (command) with those specific params (params->element1, ...)
return buffer; // buffer is a member of the class
}
unsigned char *Method2(CommandTypeEnum command, ParamsCommand2Struct *params)
{
...
}
unsigned char *Method3(CommandTypeEnum command, ParamsCommand3Struct *params)
{
...
}
unsigned char *Method4(CommandTypeEnum command, ParamsCommand4Struct *params)
{
...
}
or
unsigned char *Method(CommandTypeEnum command, void *params)
{
switch(command)
{
case CMD_1:
{
if(params == NULL) return NULL;
ParamsCommand1Struct *value = (ParamsCommand1Struct *) params;
// Construct a string (command) with those specific params (params->element1, ...)
return buffer;
}
break;
// ...
default:
break;
}
}
The main thing I do not really like of the latter option is this,
ParamsCommand1Struct *value = (ParamsCommand1Struct *) params;
because "params" could not be a pointer to "ParamsCommand1Struct" but a pointer to "ParamsCommand2Struct" or someone else.
I really appreciate your opinions!
General Answer
In Writing Solid Code, Steve Macguire's advice is to prefer distinct functions (methods) for specific situations. The reason is that you can assert conditions that are relevant to the specific case, and you can more easily debug because you have more context.
An interesting example is the standard C run-time's functions for dynamic memory allocation. Most of it is redundant, as realloc can actually do (almost) everything you need. If you have realloc, you don't need malloc or free. But when you have such a general function, used for several different types of operations, it's hard to add useful assertions and it's harder to write unit tests, and it's harder to see what's happening when debugging. Macquire takes it a step farther and suggests that, not only should realloc just do _re_allocation, but it should probably be two distinct functions: one for growing a block and one for shrinking a block.
While I generally agree with his logic, sometimes there are practical advantages to having one general purpose method (often when operations is highly data-driven). So I usually decide on a case by case basis, with a bias toward creating very specific methods rather than overly general purpose ones.
Specific Answer
In your case, I think you need to find a way to factor out the common code from the specifics. The switch is often a signal that you should be using a small class hierarchy with virtual functions.
If you like the single method approach, then it probably should be just a dispatcher to the more specific methods. In other words, each of those cases in the switch statement simply call the appropriate Method1, Method2, etc. If you want the user to see only the general purpose method, then you can make the specific implementations private methods.
Generally, it's better to offer separate functions, because they by their prototype names and arguments communicate directly and visibly to the user that which is available; this also leads to more straightforward documentation.
The one time I use a multi-purpose function is for something like a query() function, where a number of minor query functions, rather than leading to a proliferation of functions, are bundled into one, with a generic input and output void pointer.
In general, think about what you're trying to communicate to the API user by the API prototypes themselves; a clear sense of what the API can do. He doesn't need excessive minutae; he does need to know the core functions which are the entire point of having the API in the first place.
First off, you need to decide which language you are using. Tagging the question with both C and C++ here makes no sense. I am assuming C++.
If you can create a generic function then of course that is preferable (why would you prefer multiple, redundant functions?) The question is; can you? However, you seem to be unaware of templates. We need to see what you have omitted here to tell if you if templates are suitable however:
// Construct a string (command) with those specific params (params->element1, ...)
In the general case, assuming templates are appropriate, all of that turns into:
template <typename T>
unsigned char *Method(CommandTypeEnum command, T *params) {
// more here
}
On a side note, how is buffer declared? Are you returning a pointer to dynamically allocated memory? Prefer RAII type objects and avoid dynamically allocating memory like that if so.
If you are using C++ then I would avoid using void* as you don't really need to. There is nothing wrong with having multiple methods. Note that you don't actually have to rename the function in your first set of examples - you can just overload a function using different parameters so that there is a separate function signature for each type. Ultimately, this kind of question is very subjective and there are a number of ways of doing things. Looking at your functions of the first type, you would perhaps be well served by looking into the use of templated functions
You could create a struct. That's what I use to handle console commands.
typedef int (* pFunPrintf)(const char*,...);
typedef void (CommandClass::*pKeyFunc)(char *,pFunPrintf);
struct KeyCommand
{
const char * cmd;
unsigned char cmdLen;
pKeyFunc pfun;
const char * Note;
long ID;
};
#define CMD_FORMAT(a) a,(sizeof(a)-1)
static KeyCommand Commands[]=
{
{CMD_FORMAT("one"), &CommandClass::CommandOne, "String Parameter",0},
{CMD_FORMAT("two"), &CommandClass::CommandTwo, "String Parameter",1},
{CMD_FORMAT("three"), &CommandClass::CommandThree, "String Parameter",2},
{CMD_FORMAT("four"), &CommandClass::CommandFour, "String Parameter",3},
};
#define AllCommands sizeof(Commands)/sizeof(KeyCommand)
And the Parser function
void CommandClass::ParseCmd( char* Argcommand )
{
unsigned int x;
for ( x=0;x<AllCommands;x++)
{
if(!memcmp(Commands[x].cmd,Argcommand,Commands[x].cmdLen ))
{
(this->*Commands[x].pfun)(&Argcommand[Commands[x].cmdLen],&::printf);
break;
}
}
if(x==AllCommands)
{
// Unknown command
}
}
I use a thread safe printf pPrintf, so ignore it.
I don't really know what you want to do, but in C++ you probably should derive multiple classes from a Formatter Base class like this:
class Formatter
{
virtual void Format(unsigned char* buffer, Command command) const = 0;
};
class YourClass
{
public:
void Method(Command command, const Formatter& formatter)
{
formatter.Format(buffer, command);
}
private:
unsigned char* buffer_;
};
int main()
{
//
Params1Formatter formatter(/*...*/);
YourClass yourObject;
yourObject.Method(CommandA, formatter);
// ...
}
This removes the resposibility to handle all that params stuff from your class and makes it closed for changes. If there will be new commands or parameters during further development you don't have to modifiy (and eventually break) existing code but add new classes that implement the new stuff.
While not full answer this should guide you in correct direction: ONE FUNCTION ONE RESPONSIBILITY. Prefer the code where it is responsible for one thing only and does it well. The code whith huge switch statement (which is not bad by itself) where you need cast void * to some other type is a smell.
By the way I hope you do realise that according to standard you can only cast from void * to <type> * only when the original cast was exactly from <type> * to void *.