Variadic Templates before C++11 - c++

How did Boost implement Tuple before C++11 and Variadic Templates?
In other words:
Is it possible to implement a Variadic Templates class or function by not using built-in Variadic Templates feature in C++11?

Boost had a limit for the size of the tuple. As in most real-world scenarios you don't need more than 10 elements, you won't mind this limitation. As a library maintainer, I guess, the world became much simpler with variadic templates. No more macro hacks...
Here is an insightful discussion about the size limit of Boost tuple and its implementation:
boost tuple: increasing maximum number of elements
To answer your second question: No, it is not possible. At least not for an unlimited number of elements.

There are 2 common use cases I've seen, as a library developer, for variadic templates. You can build a work around for both.
Case 1: Function objects
std::function<> and lambdas are very nice, but even c++11 only gives you a fairly basic set of things you can do with them "out of the box". To implement really cool things and utilities on top of them, you need to support variadic templates because std::function can be used with any normal function signature.
Workaround:
A recursive call using std::bind is your friend. It IS less efficient than real variadic templates (and some tricks like perfect forwarding probably won't work), but it'll work okay for modest #s of template arguments until you port to c++11.
Case 2: Ordinary classes
Sometimes you need an ordinary class to manage generic std::function<>s (see above) or expose an API like "printf". Workarounds here come down to details and what each API of the class is doing.
APIs that merely manipulate variadic template data but don't need to store it can run as recursive calls. You need to write them so that they "consume" one argument at a time, and stop when they run out of arguments.
APIs (including constructors) that need to STORE variadic template data are harder- you're screwed if the types are really unlimited and could be anything. BUT, if they're always going to be primitives that map deterministically to binary, you can do it. Just write a "Serialize" call taking all the types you support, then use it to serialize the entire set into a binary buffer and build a vector of "type info" data you use to fetch & set them. Its actually a better solution than std::tuple in terms of memory and performance in the special cases its available.
Here's the "serialize tuple" trick:
// MemoryBuffer: A basic byte buffer w/ its size
class MemoryBuffer {
private:
void* buffer;
int size;
int currentSeekPt;
protected:
void ResizeBuffer() {
int newSz = size << 1; // Multiply by 2
void* newBuf = calloc( newSz, 1); // Make sure it is zeroed
memcpy( newBuf, buffer, target->size);
free( buffer);
size = newSz;
buffer = newBuf;
}
public:
MemoryBuffer(int initSize)
: buffer(0), size(initSize), currentSeekPt(0)
{
buffer = calloc( size, 1);
}
~MemoryBuffer() {
if(buffer) {
free( buffer);
}
}
// Add data to buffer
bool AddData(const void* data, int dataSz) {
if(!data || !dataSz) return false;
if(dataSz + currentSeekPt > size) { // resize to hold data
ResizeBuffer();
}
memcpy( buffer, data, dataSz);
return true;
}
void* GetDataPtr() const { return buffer; }
int GetSeekOffset() const { return currentSeekPt; }
int GetTotalSize() const { return size; }
};
struct BinaryTypeInfo {
std::type_info type; // RTTI type_info struct. You can use an "enum"
// instead- code will be faster, but harder to maintain.
ui64 bufferOffset; // Lets me "jump" into the buffer to
}
// Versions of "Serialize" for all 'tuple' data types I support
template<typename BASIC>
bool Serialize(BASIC data, MemoryBuffer* target,
std::vector<BinaryTypeInfo>& types)
{
// Handle boneheads
if(!target) return false;
// Setup our type info structure
BinaryTypeInfo info;
info.type = typeid(data);
info.bufferOffset = target->GetSeekOffset();
int binarySz = sizeof(data);
void* binaryVersion = malloc( binarySz);
if(!binaryVersion) return false;
memcpy( binaryVersion, &data, binarySz); // Data type must support this
if(!target->AddData( binaryVersion, binarySz)) {
free( binaryVersion);
return false;
}
free( binaryVersion);
// Populate type vector
types.push_back( info);
return true;
}
This is just a quick & dirty version; you'd hide the real thing better and probably combine the pieces into 1 reusable class. Note that you need a special version of Serialize() if you wish to handle std::string and more complex types.

Related

is there a way to store a generic templated function pointer?

The Goal:
decide during runtime which templated function to use and then use it later without needing the type information.
A Partial Solution:
for functions where the parameter itself is not templated we can do:
int (*func_ptr)(void*) = &my_templated_func<type_a,type_b>;
this line of code can be modified for use in an if statement with different types for type_a and type_b thus giving us a templated function whose types are determined during runtime:
int (*func_ptr)(void*) = NULL;
if (/* case 1*/)
func_ptr = &my_templated_func<int, float>;
else
func_ptr = &my_templated_func<float, float>;
The Remaining Problem:
How do I do this when the parameter is a templated pointer?
for example, this is something along the lines of what I would like to do:
int (*func_ptr)(templated_struct<type_a,type_b>*); // This won't work cause I don't know type_a or type_b yet
if (/* case 1 */) {
func_ptr = &my_templated_func<int,float>;
arg = calloc(sizeof(templated_struct<int,float>, 1);
}
else {
func_ptr = &my_templated_func<float,float>;
arg = calloc(sizeof(templated_struct<float,float>, 1);
}
func_ptr(arg);
except I would like type_a, and type_b to be determined during runtime. I see to parts to the problem.
What is the function pointers type?
How do I call this function?
I think I have the answer for (2): simply cast the parameter to void* and the template function should do an implicit cast using the function definition (lease correct me if this won't work as I think it will).
(1) is where I am getting stuck since the function pointer must include the parameter types. This is different from the partial solution because for the function pointer definition we were able to "ignore" the template aspect of the function since all we really need is the address of the function.
Alternatively there might be a much better way to accomplish my goal and if so I am all ears.
Thanks to the answer by #Jeffrey I was able to come up with this short example of what I am trying to accomplish:
template <typename A, typename B>
struct args_st {
A argA;
B argB;
}
template<typename A, typename B>
void f(struct args_st<A,B> *args) {}
template<typename A, typename B>
void g(struct args_st<A,B> *args) {}
int someFunction() {
void *args;
// someType needs to know that an args_st struct is going to be passed
// in but doesn't need to know the type of A or B those are compiled
// into the function and with this code, A and B are guaranteed to match
// between the function and argument.
someType func_ptr;
if (/* some runtime condition */) {
args = calloc(sizeof(struct args_st<int,float>), 1);
f((struct args_st<int,float> *) args); // this works
func_ptr = &g<int,float>; // func_ptr should know that it takes an argument of struct args_st<int,float>
}
else {
args = calloc(sizeof(struct args_st<float,float>), 1);
f((struct args_st<float,float> *) args); // this also works
func_ptr = &g<float,float>; // func_ptr should know that it takes an argument of struct args_st<float,float>
}
/* other code that does stuff with args */
// note that I could do another if statement here to decide which
// version of g to use (like I did for f) I am just trying to figure out
// a way to avoid that because the if statement could have a lot of
// different cases similarly I would like to be able to just write one
// line of code that calls f because that could eliminate many lines of
// (sort of) duplicate code
func_ptr(args);
return 0; // Arbitrary value
}
Can't you use a std::function, and use lambdas to capture everything you need? It doesn't appear that your functions take parameters, so this would work.
ie
std::function<void()> callIt;
if(/*case 1*/)
{
callIt = [](){ myTemplatedFunction<int, int>(); }
}
else
{
callIt = []() {myTemplatedFunction<float, float>(); }
}
callIt();
If I understand correctly, What you want to do boils down to:
template<typename T>
void f(T)
{
}
int somewhere()
{
someType func_ptr;
int arg = 0;
if (/* something known at runtime */)
{
func_ptr = &f<float>;
}
else
{
func_ptr = &f<int>;
}
func_ptr(arg);
}
You cannot do that in C++. C++ is statically typed, the template types are all resolved at compile time. If a construct allowed you to do this, the compiler could not know which templates must be instanciated with which types.
The alternatives are:
inheritance for runtime polymorphism
C-style void* everywhere if you want to deal yourself with the underlying types
Edit:
Reading the edited question:
func_ptr should know that it takes an argument of struct args_st<float,float>
func_ptr should know that it takes an argument of struct args_st<int,float>
Those are incompatible. The way this is done in C++ is by typing func_ptr accordingly to the types it takes. It cannot be both/all/any.
If there existed a type for func_ptr so that it could take arguments of arbitrary types, then you could pass it around between functions and compilation units and your language would suddenly not be statically typed. You'd end up with Python ;-p
Maybe you want something like this:
#include <iostream>
template <typename T>
void foo(const T& t) {
std::cout << "foo";
}
template <typename T>
void bar(const T& t) {
std::cout << "bar";
}
template <typename T>
using f_ptr = void (*)(const T&);
int main() {
f_ptr<int> a = &bar<int>;
f_ptr<double> b = &foo<double>;
a(1);
b(4.2);
}
Functions taking different parameters are of different type, hence you cannot have a f_ptr<int> point to bar<double>. Otherwise, functions you get from instantiating a function template can be stored in function pointers just like other functions, eg you can have a f_ptr<int> holding either &foo<int> or &bar<int>.
Disclaimer: I have already provided an answer that directly addresses the question. In this answer, I would like to side-step the question and render it moot.
As a rule of thumb, the following code structure is an inferior design in most procedural languages (not just C++).
if ( conditionA ) {
// Do task 1A
}
else {
// Do task 1B
}
// Do common tasks
if ( conditionA ) {
// Do task 2A
}
else {
// Do task 2B
}
You seem to have recognized the drawbacks in this design, as you are trying to eliminate the need for a second if-else in someFunction(). However, your solution is not as clean as it could be.
It is usually better (for code readability and maintainability) to move the common tasks to a separate function, rather than trying to do everything in one function. This gives a code structure more like the following, where the common tasks have been moved to the function foo().
if ( conditionA ) {
// Do task 1A
foo( /* arguments might be needed */ );
// Do task 2A
}
else {
// Do task 1B
foo( /* arguments might be needed */ );
// Do task 2B
}
As a demonstration of the utility of this rule of thumb, let's apply it to someFunction(). ... and eliminate the need for dynamic memory allocation ... and a bit of cleanup ... unfortunately, addressing that nasty void* is out-of-scope ... I'll leave it up to the reader to evaluate the end result. The one feature I will point out is that there is no longer a reason to consider storing a "generic templated function pointer", rendering the asked question moot.
// Ideally, the parameter's type would not be `void*`.
// I leave that for a future refinement.
void foo(void * args) {
/* other code that does stuff with args */
}
int someFunction(bool condition) {
if (/* some runtime condition */) {
args_st<int,float> args;
foo(&args);
f(&args); // Next step: pass by reference instead of passing a pointer
}
else {
args_st<float,float> args;
foo(&args);
f(&args); // Next step: pass by reference instead of passing a pointer
}
return 0;
}
Your choice of manual memory management and over-use of the keyword struct suggests you come from a C background and have not yet really converted to C++ programming. As a result, there are many areas for improvement, and you might find that your current approach should be tossed. However, that is a future step. There is a learning process involved, and incremental improvements to your current code is one way to get there.
First, I'd like to get rid of the C-style memory management. Most of the time, using calloc in C++ code is wrong. Let's replace the raw pointer with a smart pointer. A shared_ptr looks like it will help the process along.
// Instead of a raw pointer to void, use a smart pointer to void.
std::shared_ptr<void> args;
// Use C++ memory management, not calloc.
args = std::make_shared<args_st<int,float>>();
// or
args = std::make_shared<args_st<float,float>>();
This is still not great, as it still uses a pointer to void, which is rarely needed in C++ code unless interfacing with a library written in C. It is, though, an improvement. One side effect of using a pointer to void is the need for casts to get back to the original type. This should be avoided. I can address this in your code by defining correctly-typed variables inside the if statement. The args variable will still be used to hold your pointer once the correctly-typed variables go out of scope.
More improvements along this vein can come later.
The key improvement I would make is to use the functional std::function instead of a function pointer. A std::function is a generalization of a function pointer, able to do more albeit with more overhead. The overhead is warranted here in the interest of robust code.
An advantage of std::function is that the parameter to g() does not need to be known by the code that invokes the std::function. The old style of doing this was std::bind, but lambdas provide a more readable approach. Not only do you not have to worry about the type of args when it comes time to call your function, you don't even need to worry about args.
int someFunction() {
// Use a smart pointer so you do not have to worry about releasing the memory.
std::shared_ptr<void> args;
// Use a functional as a more convenient alternative to a function pointer.
// Note the lack of parameters (nothing inside the parentheses).
std::function<void()> func;
if ( /* some runtime condition */ ) {
// Start with a pointer to something other than void.
auto real_args = std::make_shared<args_st<int,float>>();
// An immediate function call:
f(real_args.get());
// Choosing a function to be called later:
// Note that this captures a pointer to the data, not a copy of the data.
// Hence changes to the data will be reflected when this is invoked.
func = [real_args]() { g(real_args.get()); };
// It's only here, as real_args is about to go out of scope, where
// we lose the type information.
args = real_args;
}
else {
// Similar to the above, so I'll reduce the commentary.
auto real_args = std::make_shared<args_st<float,float>>();
func = [real_args]() { g(real_args.get()); };
args = real_args;
}
/* other code that does stuff with args */
/* This code is probably poor C++ style, but that can be addressed later. */
// Invoke the function.
func();
return 0;
}
Your next step probably should be to do some reading on these features so you understand what this code does. Then you should be in a better position to leverage the power of C++.

C++ template to read value from member variable or member function

I am writing code generator and using flatbuffers for generating classes. The rest of the code generator will work with these classes in C++.
I have not been able to figure out how to keep the API consistent for reading data for two different types of classes that flatbuffer may generate. I am using the object api (testRecordT) in the example for whenever an object needs to be written to (and can be read back as well) and flatbuffer overlay for when the data can only be read from.
I have not been able to get any template or free functions to work to give me a consistent api that would work in both the cases.
Below is a snippet of what I am trying to get to work.
struct testRecordT {
int32_t field1;
std::string field2;
};
struct testRecord {
int32_t field1() const {
return 0;
// flatbuffer generated - return GetField<int32_t>(VT_FIELD1, 0);
}
const flatbuffers::String *field2() const {
return nullptr;
// flatbuffer generated - return GetPointer<const flatbuffers::String *>(VT_FIELD3);
}
};
void Test() {
testRecordT * members; // assume pointers are valid
testRecord * memberFunctions;
// Need to be able to create a read function/template that would work. This would simplify the code generation a lot. I can generate either one below, as long as it is consistent in both cases.
auto r = read(members->field1); // or read(members,field1)
auto v = read(memberFunctions->field1); // or read(memberFunctions,field1)
}
The read functions or template functions should be consistent. Any pointers or thoughts would be helpful. I am using C++17 with gcc 7.3.1 .
You can use std::invoke for this. It can both call functions or access members.
auto r = std::invoke(&testRecordT::field1, members);
auto v = std::invoke(&testRecord::field1, memberFunctions);
You can use std::invoke() for this problem.

Create dynamically null terminated parameters for C binding

For my project I am binding a C++ method to a C function.
This function is from gstreamer and is variadic in this form :
GstElement *gst_element_link_many(GstElement *el1, GstElement* el2 ,[...], nullptr);
Let's say I want to pass a vector to my bindings, what would be your approach ? Ideally I would have this result
void linkElements(std::vector<GstElement*>& elements) {
[...]
gst_element_link_many(elementList... , nullptr);
}
I am thinking of parameter packs but I am unsure of how to implement those.
Thanks !
Edit: I can't modify the gstreamer function so I can't pass a pointer. Gstreamer as a gst_element_link(GstElement* el1, GstElement *el2); function but it behaves differently because it links elements 2 by 2 and thus computes capabilities of each pair independently.
Parameter packs are a compile-time construct, while a vector is a runtime construct. This makes parameter packs irrelevant for this question. There are several solutions, short of redesigning the interface of the C function.
The first option is given in
M Oehm's answer to Passing all elements of an array to a function with variable parameters (…) mentions the technique of one big switch:
void linkElements(std::vector<GstElement*>& elements) {
switch (elements.size()) {
case 0: return gst_element_link_many(nullptr);
case 1: return gst_element_link_many(elements[0], nullptr);
case 2: return gst_element_link_many(elements[0], elements[1], nullptr);
case 3: return gst_element_link_many(elements[0], elements[1], elements[2], nullptr);
case 4: return gst_element_link_many(elements[0], elements[1], elements[2], elements[3], nullptr);
... and so on for how long one wants to support
default:
throw std::runtime_error(std::to_string(elements.size()) + " elements can't be passed (too many elements"));
}
The disadvantage is that this method defines the maximal number of parameters at compile time.
The second option is to automate the switch statement. It uses recursion, so it may be less efficient than the other options, but it is very easy to extend to more parameters:
#include <iostream>
#include <string>
#include <cstdio>
#include <vector>
#include <utility>
#include <tuple>
template <unsigned size, class Func, class Type, std::size_t... I>
void call_n(Func func, const std::vector<Type> & vec, std::index_sequence<I...>)
{
func(vec[I]...);
}
template <unsigned size, class Func, class Type>
auto call_n(Func func, const std::vector<Type> & vec)
{
return call_n<size>(func, vec, std::make_index_sequence<size>());
}
template <unsigned min, unsigned max, class Func, class Type>
void call_max_n(Func func, std::vector<Type> & elements)
{
if (elements.size() == min) {
call_n<min>(func, elements);
return;
}
if constexpr(min < max)
call_max_n<min+1, max>(func, elements);
else
throw std::runtime_error("Too many elements");
}
int main()
{
std::vector<const char*> elements{"%s %s %s", "hello", "nice", "world"};
call_max_n<1, 4>(std::printf, elements);
}
You can try it out on wandbox. From my tests, gcc is able to create a flat function. Maybe fore more complicated examples it will actually use recursion but, regardless or that, the complexity is O(n) just as if it was called without any recursion.
(EDIT: replaced the O(n2) algorithm with the linear algorithm shown above).
The third option is given in Matt Joiner's answer to "Passing parameters dynamically to variadic functions" mentions a C library that can be used to convert a vector into variadic templates:
FFCALL is a library which provides wrappers for passing parameters
dynamically to variadic functions. The group of functions you're
interested in is avcall.
The above links are outdated, and this link seems to be more up to date.
From the way I understand the documentation, your code should look like:
#include <avcall.h>
void linkElements(std::vector<GstElement*> & elements) {
av_alist alist;
av_start_void(alist, &gst_element_link_many);
for (auto ptr: elements) {
av_ptr(alist, GstElement*, ptr);
}
av_ptr(alist, GstElement*, nullptr);
av_call(alist);
}
I am not sure how portable this is. It seems to work on Linux Intel machines (both 32 and 64 bits). Maybe it can also work on Windows. If it does not work on your system, then I think it is not too difficult to port it to your system.
And the last option is to use assembly. It is possible to put the data from the array into the correct registers and/or the stack. This is not very complicated, and can be found here for the Intel architecture.
Unfortunately, all the flexible solutions are not purely C++ and require some add on (either from a library, or from an assembly code).
EDIT: I have added one of the solutions to github, and I intend to all all of the above solutions.

Dynamic Function Args for Callback / RPC in C++

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.

Several specific methods or one generic method?

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 *.