I have a third party closed source c library that interfaces with hardware. The library has some api functions that accept void *
arguments to read/write and configure some io, like so:
int iocall(int cmd, void * args);
int iorw(int cmd, void * buff, size_t buff_size);
I want to wrap these in a c++ class to be able to inject that class, be able to mock it using gtest and introduce exceptions and get rid of all the return value checks inside the upper service classes. So far so good. Here comes my question: What would be the best way of designing an interface for that class when it comes to the void *
arguments?
interface.h
class wrap:
{
virtual void wrap_iocall(int cmd, ??) = 0;
}
interface_impl.h
class wrap:
{
void wrap_iocall(int cmd, ??) override
{
int ret{iocall(cmd, ??)};
// do some more stuff, e.g. exceptions and so on
}
}
My first take was to just overload the calls with dedicated types - but since there are a lot of them, this might be a pain to maintain and I will need to change the interface when another call is needed (e.g. other hardware call) + the library might get updates which forces me to update types.
The second thing I thought about is using std::any
which would fit my use case but I am unsure how I would pass a pointer to the std::any
container to the underlying c-function? I thought about pushing it into a std::vector<std::any>
and use the .data()
function to pass a pointer, but then I am left with a guess on the size of that container I think? And this sounds like a very bad attempt to achieve what I am looking for.
The third thing I came across was a solution using templates, but if my understanding is correct, these things cannot be virtual and hence break with my intention to mock the interface, requiring it to be virtual. So I think this might not work either.
The last thing I can currently think of is sticking to just void *
in the wrapping function and have the upper service layer handle the type casts, e.g. allocate the struct for the specific device call and pass the pointer to the void *
argument of the wrapper. This is by far my least favorite option.
I want to stick to the principle of type safe function arguments but I am not sure of how to proceed from here. Any help/feedback is appreciated!
Thank you all for your suggestions and comments. I have chosen to implement the calls as seperate functions as suggested.Since they do depend on the cmd
value, I used that field for dedicated naming of the functions + the appropriate struct. I wrote a small python script to automate the code creation.
Again, thank you all for the fast response!
Cheers!