So I am using this telegram/message dispatcher system for my A.I. which comes from Matt Buckland's "Programming Game A.I. by Example" book.
I have this method for the MessageDispatcher class:
void DispatchMsg(double delay, int sender, int receiver, int msg, void *additionalInfo = nullptr);
Which then uses a Telegram struct:
struct Telegram
{
// Messages can be either dispatched immediately or delayed for
// a specified amount of time. If a delay is necessary, this
// field is stamped with the time the message should be dispatched.
double DispatchTime;
// Who is sending this message
int Sender;
// Who should the component give this message to
// may be set to -1 if not required
int Receiver;
// Message itself, should be one of the several enumerated types
int Msg;
// Additional information that might want to be passed along
void *ExtraInfo;
Telegram():DispatchTime(-1),
Sender(-1),
Receiver(-1),
Msg(-1)
{
// Empty
}
Telegram(double time,
int sender,
int receiver,
int msg,
void *info = nullptr):DispatchTime(time),
Sender(sender),
Receiver(receiver),
Msg(msg),
ExtraInfo(info)
{
// Empty
}
};
With a cast like:
template <class T>
inline T DereferenceToType(void *p)
{
return *(T*)(p);
}
The trouble lies here:
void Player::playerFeed() {
if (!Target)
return;
Courier->DispatchMsg(SEND_MSG_IMMEDIATELY, PLAYER_ID, TargetedActor, MessageType::PLAYER_FED, &ActorNode->getPosition());
}
Where ActorNode->getPosition() is from Ogre3d Ogre::SceneNode:
virtual const Vector3 & getPosition (void) const
Gets the position of the node relative to it's parent.
I then get it back doing:
Ogre::Vector3 attackerPos = DereferenceToType<Ogre::Vector3>(msg.ExtraInfo);
I would prefer to use a const Ogre::Vector3 here and that can be done writing a const dereference helper function.
Anyway, the problem is:
xxx|90|warning: invalid conversion from 'const void*' to 'void*' [-fpermissive]|
I understand the warning; but I'm not sure how to correct the problem.
I tried fixing it by writing a second method for DispatchMsg:
void DispatchMsg(double delay, int sender, int receiver, int msg, void *additionalInfo = nullptr);
// For const void*
void DispatchMsg(double delay, int sender, int receiver, int msg, const void *additionalInfo);
But that moved the warning into the function at the creation of the Telegram.
So I tried some things like making a second parameter in my Telegram structure called const void *ConstExtraInfo, the trouble is this seems to make the Telegram structure messy imo.
Basically my question is: is there a clean way implementation for this or if it must be done by having extra members within the Telegram to find out which type of information is stored?
Can it be done with a template for void* or const void* like: Telegram or would this complicate the receiving end of the telegram?
Please let me know if I need to post more information on this.
Thanks.
Your first problem is that the additionalInfo-pointer should be const-qualified.
Then, your template should also use const.
Finally, it should return a reference and not copy the data:
template <class T> inline const T& DereferenceToType(const void *p)
{
return *(const T*)p;
}
Anyway, why hide the cast? Instead, do it like this in the receipient:
const auto& extra = *(T*)p;
You could use a const_cast or a plain C style cast to throw away const. This would be the quickest and dirtiest way.
Now, what you are really trying to achieve here is to covert to an intermediate data type that can be cast back and forth from an abstract type to a concrete type. One possible way of doing this is to use what some refer to as a Variant, which is a class/struct that holds some opaque data and a tag that identifies this data. Something similar to:
enum DataTag {
DATA_INT,
DATA_STRING,
DATA_VEC3,
// etcetera
};
struct Variant {
virtual DataTag GetTypeTag() const = 0;
virtual int AsInt() const = 0;
virtual string AsString() const = 0;
virtual Vec3 AsVector() const = 0;
// same goes for assigning a value. I.e: FromInt()/FromString()
};
template<class T>
struct VariantImpl : public Variant {
// add constructors as needed
VariantImpl(const T & data, DataTag tag)
{
this->data = data;
this->tag = tag;
}
// implement the proper conversions
int AsInt() const { }
string AsString() const { }
Vec3 AsVector() const { }
DataTag GetTypeTag() const { return tag; }
T data;
DataTag tag;
};
Then you could have a pointer to a Variant in the Telegram struct and set the ExtraInfo with:
telegram->ExtraInfo = new VariantImpl<int>(42);
And then access it at any time with AsInt(), as long as you check the type tag first to ensure the conversion is allowed.
From this, you can add a lot to it to fit your needs. Hope it helps.
Related
I'm trying to serialize simple single level classes like the ones bellow, without external libraries like boost, and without having to implement serializer function for every class. Even though I have so few classes that I could easily implement a serializer for each one, for future reference, I would like to have at hand a simple solution that scales well.
The requirement for each class to be serialized is that its members are only serializable types, and an array of member pointers is defined, so that at serialization the members can be iterated regardless of which class is passed.
The problem is that the compilation fails because of the missing cast where the member pointer is dereferenced, obviously:
esp32.ino: 122:35: error: 'footprint.MessageFootprint<1>::Members[i]'
cannot be used as a member pointer, since it is of type 'void*
I don't know how to store the member pointers in a iterable collection or how to avoid the void* cast.
That's my goal. I want to iterate over class members at serialization having a single generic serialization function.
I don't know what to do.
enum SerializableDataTypes {
SerInt,
SerFloat,
SerString,
SerIntArray
};
template <int N>
struct MessageFootprint {
SerializableDataTypes DataTypes[N];
void* Members[N];
};
template<typename T, typename R>
void* void_cast(R(T::*m))
{
union
{
R(T::*pm);
void* p;
};
pm = m;
return p;
}
class ControlMessage{};
// first structure to be serialized
class Message1 : public ControlMessage {
public:
int prop1;
int prop2;
};
const int Message1MemberCount = 2;
const MessageFootprint<Message1MemberCount> Message1FootPrint = { { SerInt, SerInt }, {void_cast(&Message1::prop1), void_cast(&Message1::prop2)} };
// second structure to be serialized
class Message2 : public ControlMessage {
public:
int prop1;
String prop2;
};
const int Message2MemberCount = 2;
const MessageFootprint<Message2MemberCount> Message2FootPrint = { { SerInt, SerInt }, {void_cast(&Message2::prop1), void_cast(&Message2::prop2)} };
template<int N>
void SerializeMessage(MessageFootprint<N> footprint, ControlMessage message) {
for (int i = 0; i < N; i++) {
if (footprint.DataTypes[i] == SerInt) {
// serialization code here based on data type
// for demonstration purposes it's only written in the serial port
logLine(String(i));
Serial.println(*((int*)(message.*(footprint.Members[i]))));
}
}
}
void main() {
// usage example
Message1 msg = Message1();
msg.prop1 = 1;
msg.prop2 = 2;
SerializeMessage(Message1FootPrint, msg);
}
Don't erase types; that is, don't cast your pointers to void*. If you preserve the types of the pointers through templates, you can choose the deserialization functions directly off their types, and thus you won't even have to specify them. Indeed, you already have a bug where you have marked the second member of Message2 SerInt when it is a String. If you work off the actual types instead of forcing the user to duplicate them, you avoid such errors. Also, the common superclass is completely unnecessary.
template<typename T, typename... Parts>
struct MessageFootprint {
std::tuple<Parts T::*...> parts;
MessageFootprint(Parts T::*... parts) : parts(parts...) { }
};
template<typename T, typename... Parts>
MessageFootprint(Parts T::*...) -> MessageFootprint<T, Parts...>; // deduction guide
// e.g.
struct Message1 {
int prop1;
int prop2;
};
inline MessageFootprint footprint1(&Message1::prop1, &Message1::prop2);
// deduction guide allows type of footprint1 to be inferred from constructor arguments
// it is actually MessageFootprint<Message1, int, int>
// if you are on a C++ standard old enough to not have deduction guides,
// you will have to manually specify them
// this is still better than letting the types be erased, because now the compiler
// will complain if you get it wrong
// e.g. if I replicate your mistake
struct Message2 {
int prop1;
std::string prop2;
};
inline MessageFootprint<Message2, int, int> footprint2(&Message2::prop1, &Message2::prop2);
// This does not go through because ^^^ is wrong
Serialization is probably best handled with overloading. For each Part T::* in a MessageFootprint<T, Part...>, extract a Part& from the T and call out to an overloaded function that decides what to do based on Part:
// I have no idea what serial port communication stuff you're doing
// but this gets the point across
void SerializeAtom(int i) { std::cout << "I" << i; }
void SerializeAtom(std::string const &s) { std::cout << "S" << s.size() << "S" << s; }
template<typename T, typename... Parts>
void SerializeFootprint(MessageFootprint<T, Parts...> footprint, T const &x) {
// calls the provided functor with the things in the tuple
std::apply(
// this lambda is a template with its own Parts2... template parameter pack
// and the argument is really Parts2... parts
// we then do a fold expression over parts
// we need std::apply because there's no simpler way to get the actual
// values out (std::get fails when there are duplicates)
[&x](auto... parts) { (SerializeAtom(x.*parts), ...); },
footprint.parts);
}
// Trying to write ^^^ before C++17 would probably be a nightmare
This system is extensible: to add a new "atomic" type, just overload SerializeAtom. No need to manage an enum or whatnot. Deserialization would mean a family of DeserializeAtom overloads that write into the given reference, and a DeserializeFootprint which would probably look exactly like SerializeFootprint.
Godbolt demonstration
I've developed a serialization system that uses buffering.
Each object inherits from an interface that declares functions for:
1. Returning the size of the object on the stream.
2. Storing the object members to a buffer.
3. Loading the object members from a buffer.
This system is based on the fact that structs and classes can contain padding and that the class/struct is most knowledgeable about its members. For example, a multibyte integer may be Big Endian in the buffer, and the object needs to convert to Little Endian. This system also accommodates different methods for writing variable length text fields.
class Binary_Stream_Interface:
{
public:
// Returns the size, in uint8_t units, that the object occupies in
// a buffer (stream), packed.
virtual size_t size_on_stream() const = 0;
// Loads the class members from a buffer, pointed to by p_buffer.
// The p_buffer pointer will be incremented after loading the object.
virtual void load_from_buffer(uint8_t* & p_buffer) = 0;
// Stores the class members to a buffer, pointed to by p_buffer.
// The p_buffer pointer will be incremented after loading the object.
virtual void store_to_buffer(uint8_t * & p_buffer) const = 0;
};
To serialize (write) an object:
1. Call size_on_stream() to determine the buffer size needed.
2. Allocate the buffer.
3. Call store_to_buffer to store the object into the buffer.
4. Write the buffer to the stream, using std::ostream::write.
5. Delete the buffer.
Reading an object:
1. Call size_on_stream() to determine the buffer size needed.
2. Allocate the buffer.
3. Read the data from the stream into the buffer, using std::istream::read and the size needed.
4. Call the load_from_buffer() method.
5. Delete the buffer.
Implementation is left as an exercise for the OP.
Note: Templates can be used for common POD and std:string to make everything more uniform.
Edit 1: Example
struct Student
: public Binary_Stream_Interface
{
std::string name;
unsigned int id;
size_t size_on_stream() const
{
size_t stream_size = sizeof(id) + sizeof(int) + name.length();
return stream_size;
}
void load_from_buffer(uint8_t* & p_buffer)
{
// Read the string size.
unsigned int length = *((unsigned int *)(p_buffer));
p_buffer += sizeof(length);
// Load the string text from the buffer
name = std::string((char *) p_buffer, length);
p_buffer += length;
id = *((unsigned int *) p_buffer);
p_buffer += sizeof(id);
}
void store_to_buffer(uint8_t * & p_buffer) const
{
unsigned int length = name.length();
*((unsigned int *) p_buffer) = length;
p_buffer += sizeof(unsigned int);
p_char_buffer = (char *) p_buffer;
std::copy(name.begin(), name.end(), p_char_buffer);
p_buffer += length;
*((unsigned int *) p_buffer) = id;
p_buffer += sizeof(unsigned int);
}
};
I myself am not exactly sure what I am asking for here, so please bear with me for a second.
Basically, I have an application that has to, amongst other things, be able to process commands that are received from some external source, let's say a socket.
Every command uses two data structures specific to this command - one structure holds a set of arguments relevant to the processing of this command, the other one receives some resulting data.
Naturally, data transfer handler class knows nothing about which command uses which structures, so the first thing that is invoked after command is received looks like this:
CSocketHandler::ReceiveCommand(int CommandCode, const TBuffer& Args, TBuffer& Result);
And the last one looks like this:
CClassBar::ProcessCommandFoo(const TFooArgs& Args, TFooResult& Result);
Now, all that's missing is the part that converts TBuffer to TFooArgs, invokes a correct method, then converts TFooResult back to TBuffer (A conversion is trivial).
The amount of different handler classes and commands promises to be quite large, so what I am looking at here is a method three miles long that does the same thing to different data types then calls different functions over and over again.
So my question - is it possible to automate this tedious and error prone task? Preferably to the point where just defining a new message handling method would be enough, but I am willing to compromise.
Generic code is good.
Create two convert methods, one from TBuffer to XArgs and the other from XResult to TBuffer
Create an automatic command wrapper
Implement a map to automatically dispatch to those wrappers
You can do it either through pointer to functions or inheritance, I suppose inheritance will be easier...
class BaseCommand {
public:
virtual ~BaseCommand() {}
virtual TBuffer invoke(TBuffer const& tb) = 0;
};
template <typename Args, typename Result>
class CommandT: public BaseCommand {
public:
virtual TBuffer invoke(TBuffer const& tb) {
Args const a = from_buffer(tb, &a); // free function
Result const r = this->invoke(a);
return to_buffer(r); // free function
}
private:
virtual Result invoke(Args const&) = 0;
};
Note: as a cheat, we pass &a to from_buffer to get automatic argument deduction, it is expected that the pointer is unused.
So, let us suppose that we have our arguments and results (both int it's easier):
int from_buffer(TBuffer const& tb, int const*) {
return tb.asInt();
}
TBuffer to_buffer(int i) {
return TBuffer(i);
}
And then we can implement a command that deals with int:
class IntCommand: public CommandT<int, int> {
virtual int invoke(int const& i) override { return i; }
};
Okay, let us move on to the dispatch. The idea is to register each command to its ID.
template <typename T>
std::unique_ptr<BaseCommand> make_command() { return std::unique_ptr<T>(new T()); }
static std::map<int, std::unique_ptr<BaseCommand>> Commands;
int main() {
Commands.insert(std::make_pair(1, make_command<IntCommand>()));
// lots of them...
// starts processing
}
And in SocketHandler we have:
void SocketHandler::ReceiveCommand(int code, TBuffer const& a, TBuffer& r) {
typedef std::map<int, std::unique_ptr<BaseCommand>>::const_iterator const_it;
const_it it = Commands.find(code);
if (it == Commands.end()) {
std::cerr << "Unknown command: " << code << "\n";
throw std::runtime_error("Unknown command");
}
r = it->second->invoke(a);
}
I have an application which will be receiving messages from another application. These messages will be XML fomatted strings, and they will contain a <messageType> tag. The message type will identify this message as a type of internal message. The following code shows my internal message structures.
namespace
Application1{
enum ApplicationAttributes{
ApplicationName = 1000,
Start,
Stop,
Pause,
Save,
Discard,
SelectRunway,
DoAlignment,
RedoAlignment,
AlignmentOK,
DoCalibrationStage1,
SetCalibrationStage1,
SetCalibrationStage2,
SetCalibrationStage3,
CancelCalibration,
CalibrationOK
};
struct Alignment{
int x;
int y;
int error;
};
struct Calibration{
int x;
int y;
int error;
};
}
alignment and calibration are the two internal message structures.
What I'm trying to do is build a 'message interpreter' which will receive an XML string, decode it and return any one of the structs shown above; so if the <messageType> is 'alignment', the message interpreter will build an alignment struct, and return that.
So ultimately, I'm trying to make a template function, which can return an arbitrary struct, based on what i read in from <messageType>.
Are my objectives clear? is my approach the right one?
Let me know if I should clarify, or if I should take a different approach.
I don't believe a template function makes sense. Your input is always going to be a string, and C++ can't differentiate function signatures based on return type alone - so I don't know how a template would help - what would the type argument be?
I'd suggest making your function a normal one that parses out the messageType and allocates a struct based on it - you can use whatever constructs you want for this.
The trick would be (in my mind) to derive all of your internal-message-classes from the same empty base class - you could then return a pointer to that base class back from your function, and it will hold whatever type got created.
It be a good idea to return an enumeration along with the pointer in a std::pair which you can use to determine the correct derived type that was created, that way you can cast the result directly to the correct derived type with a static_cast.
As I understand it your structures are known within the application, so what about this save variant:
class Message {
public:
static Message Alignment (alignment_t const &);
...
Type type() const;
int alignment() const;
private:
Message (Type t);
assert_type (Type t, const char *msg) const;
private:
Type type_;
};
Message Message::Alignment (alignment_t const &alignment)
{
Message ret (Type::Alignment);
ret.alignment_ = alignment;
return ret;
}
void Message::assert_type (Type t, const char *msg) const
{
if (type() != t) throw std::runtime_error (msg);
}
int Message::alignment() const
{
assert_type (Type::Alignment,
"alignment_x() called for non-alignment-message");
return alignment_;
}
(coded without verification to give you the idea)
This works without polymorphism (I use this pattern in a compiler for a LISP like language, where polymorphic trees would result in more complicated code). You can change it to return "alignment_x()" and so on, if you like that more.
Fully dynamic structures are not possible, and solutions that try to come near will be rather complicated. Use the most-maintainable solution.
If you write a factory function/functor for each type, you can associate that with the messageType (map<string, Factory*> will be sufficient), but what to return?
You can return some kind of discriminated union, or boost::variant, if you don't mind the top-level decoder depending on all possible message types.
But, what is the decoder going to do with this return value? If it just switches on the type and calls a type-specific callback in each case, you could invert control by attaching a callback function/functor to the factory directly.
Then the decoder doesn't return anything, it just constructs the message struct and passes it directly to a handler.
Simple implementation (OK, that was more typing than I thought):
class Decoder
{
public:
virtual ~Decoder();
virtual void decode(std::string const &xml) = 0;
};
template <typename Factory, typename Callback>
class SimpleDecoder: public Decoder
{
Factory factory;
Callback callback;
public:
SimpleDecoder(Factory f, Callback c)
: factory(f), callback(c)
{}
void decode(std::string const &xml)
{
callback( factory( xml ) );
}
};
std::map<std::string, Decoder*> factories;
template <typename F, typename C>
void registerSimpleDecoder(std::string const &n, F f, C c)
{
factories[n] = new SimpleDecoder(f, c);
}
void decodeXmlMessage(std::string const &messageType, std::string const &body)
{
factories[messageType]->decode(body);
}
using QMetaObject::newInstance, so you can create a QObject* that can be converted afterwards to your class using dynamic_cast
class MyClass : public QObject{
public:
enum Type{ MyClassType = UserType + 1 }
Q_INVOKABLE MyClass();
}
Q_DECLARE_METATYPE ( MyClass )
then, in your XML Parsing Code:
MyClass* myObject = (MyClass*) QMetaType::construct ( MyClass::MyClassType );
And things will work out.
I'm looking at some code at the moment which has been ported and is failing to compile. The code has been written in a rather 'C' like way and is passing function pointers in order to set particular mutators on an object. The object being populated is declared as follows:
class Person
{
std::string n_;
int a_;
public:
void name( const std::string& n ) { n_ = n; }
std::string name() const { return n_; }
void age( const int& a ) { a_ = a; }
int age() const { return a_; }
};
Fairly standard stuff. Then we have some interesting functions which I've trimmed for brevity:
typedef void (Person::FnSetStr)(const std::string& s);
typedef void (Person::FnSetInt)(const int& i);
void setMem( const std::string& label, Person* person, FnSetStr fn)
{
// Do some stuff to identify a std::string within a message from the label.
// assume that 'val_s' contains the string value of the tag denoted by
// the label.
(person->*fn)(val_s);
}
void setMem( const std::string& label, Person* person, FnSetInt fn)
{
// Do some stuff to identify an int within a message from the label.
// assume that 'val_i' contains the int value of the tag denoted by the
// label.
(person->*fn)(val_i);
}
And then this gets called as follows:
Person* person = new Person;
setMem("Name", person, Person::name ); // (1)
setMem("Age", person, Person::age ); // (2)
The idea seems to be to pass a label, an object and the address of an appropriate mutator. The type of the 3rd parameter is being used to get the compiler to select which overload to call and the specific overload then gets a suitable variable ready and calls the function passing it as a parameter to set the value on the object.
This workled on an old Solaris compiler. However, when it compiles on GCC, I get failures at points (1) and (2):
error: no matching function for call to
'setMem( const std::string& label, Person* person, <unknown type> )'
It looks like the new compiler treats, say, Person::age as a type rather than a pointer to a function and cannot resolve the overload. I am looking at changing the code to use a function object rather than straight pointers to functions.
I wanted to know whether there's a way that the calling code can stay like this (i.e. without an explicitly stating the type that the function takes) bearing in mind that I can't change the Person class and would ideally like to keep changes to a minimum.
First change the declaration:
typedef void (Person::*FnSetStr)(const std::string& s);
typedef void (Person::*FnSetInt)(const int& i);
Then change the call:
setMem("Name", person, &Person::name ); // (1)
setMem("Age", person, &Person::age ); // (2)
Builds clean at warning level 4 in VS 2010.
I am working on implementing a function that would execute another function a few seconds in the future, depending upon the user's input. I have a priority queue of a class (which I am calling TimedEvent) that contains a function pointer to the action I want it to execute at the end of the interval. Say for instance that the user wants the program to call a function "xyz" after 3 seconds, they would create a new TimedEvent with the time and the function pointer to xyz and add it to the priority queue (which is sorted by time, with the soonest events happening first).
I have been able to successfully get the priority queue to pop off the top element after the specified time, but am running into a wall here. The functions I want to call could take a variety of different parameters, from ones that take only a single integer to ones that take 3 integers, a string, etc. and also return different values (some ints, some strings, etc.). I have looked into va_lists (which I have no experience with), but this doesn't seem to be the answer, unless I'm missing something.
In summary (the TL;DR version):
I would like to be able to call these functions as "diverse" as these with the same function pointer:
void func1(int a, int b);<br/>
int func2(int a, string b, OtherClass c);
Am I on the right track with a va_list and a function callback? Can this be implemented easily (or at all)?
Thanks!
I'm inferring here that these functions are API calls that you have no control over. I hacked up something that I think does more or less what you're looking for; it's kind of a rough Command pattern.
#include <iostream>
#include <string>
using namespace std;
//these are the various function types you're calling; optional
typedef int (*ifunc)(const int, const int);
typedef string (*sfunc)(const string&);
// these are the API functions you're calling
int func1(const int a, const int b) { return a + b; }
string func2(const string& a) { return a + " world"; }
// your TimedEvent is given one of these
class FuncBase
{
public:
virtual void operator()() = 0;
};
// define a class like this for each function type
class IFuncWrapper : public FuncBase
{
public:
IFuncWrapper(ifunc fp, const int a, const int b)
: fp_(fp), a_(a), b_(b), result_(0) {}
void operator()() {
result_ = fp_(a_, b_);
}
int getResult() { return result_; }
private:
ifunc fp_;
int a_;
int b_;
int result_;
};
class SFuncWrapper : public FuncBase
{
public:
SFuncWrapper(sfunc fp, const string& a)
: fp_(fp), a_(a), result_("") {}
void operator()() {
result_ = fp_(a_);
}
string getResult() { return result_; }
private:
sfunc fp_;
string a_;
string result_;
};
int main(int argc, char* argv[])
{
IFuncWrapper ifw(func1, 1, 2);
FuncBase* ifp = &ifw;
// pass ifp off to your TimedEvent, which eventually does...
(*ifp)();
// and returns.
int sum = ifw.getResult();
cout << sum << endl;
SFuncWrapper sfw(func2, "hello");
FuncBase* sfp = &sfw;
// pass sfp off to your TimedEvent, which eventually does...
(*sfp)();
// and returns.
string cat = sfw.getResult();
cout << cat << endl;
}
If you have a lot of functions returning the same type, you can define a subclass of FuncBase that implements the appropriate GetResult(), and wrappers for those functions can subclass it. Functions returning void would not require a GetResult() in their wrapper class, of course.
I think boost::bind will be useful to you. For your application, you will probably want to bind all arguments when you create the functor, before putting it on the queue (that is, not use any _1 or _2 placeholders). I don't think you need anything as complicated as lambda expressions/abstractions, but it's good to understand what they are.
+1 ceo for the DIY approach. That will work too, but you have to do all the hard work yourself.
If you want to DIY though, I would suggest using templates instead of defining an xfunc and XFuncWrapper for each combination of types (see code below).
Also, I think allowing different return types is going to be pointless -- whatever code is de-queuing and calling the functions is going to be generic. Either it expects the same type of return from each function, or it expects them to be procedures (return void).
template<typename R>
class FuncWrapper0 : public FuncBase
{
public:
typedef R (*func)();
FuncWrapper0(func fp) : fp_(fp) { }
void operator()() { result_ = fp_(); }
R getResult() { return result_; }
private:
func fp_;
R result_;
};
template<typename R, typename P1>
class FuncWrapper1 : public FuncBase
{
public:
typedef R (*func)(const P1 &);
FuncWrapper1(func fp, const P1 &p1) : fp_(fp), p1_(p1) { }
void operator()() { result_ = fp_(p1_); }
R getResult() { return result_; }
private:
func fp_;
P1 p1_;
R result_;
};
template<typename R, typename P1, typename P2>
class FuncWrapper2 : public FuncBase
{
public:
typedef R (*func)(const P1 &, const P2 &);
FuncWrapper2(func fp, const P1 &p1, const P2 &p2)
: fp_(fp), p1_(p1), p2_(p2) { }
void operator()() { result_ = fp_(p1_, p2_); }
R getResult() { return result_; }
private:
func fp_;
P1 p1_;
P2 p2_;
R result_;
};
What you're trying to do is almost impossible to get to work. You might want to consider packing your parameters into something like an std::vector<boost::any> instead.
Variable parameter lists are really the opposite of what you want. A variable parameter list allows a single function to be called from multiple sites, each with a unique set of parameters. What you want is to call multiple functions from a single site, each with a unique set of parameters -- and a variable parameter list just doesn't support that.
c/invoke is a library that lets you construct arbitrary function calls at runtime, but I think that's overkill in this case. It sounds like you should find a way to "normalize" the callback function's signature so that you can call it the same way every time with a list, structure, union or something that allows you to pass different data through the same interface.
Well, there is a real hardcore trick that exploits the fact that in C every function is a pointer and you can cast a pointer to any other pointer. The original code, where I got this from, was written, when compilers didn't gave errors on implicit casts, so it took me a while to figure out that I had to cast the functions. What it does is that it casts the callback function to a function with a variable number of arguments. But at the same time, the invocation function is cast to a function with 10 arguments, of which not all will be supplied. Especially this last step seems tricky, but you've seen it before, where you give the wrong number of arguments to printf and it just compiles. It might even be that this is what va_start/va_end does under the hood. The code is actually for doing a custom operation on any element in the database, but it could be used for your situation as well:
#include <stdio.h>
typedef int (*INTFUNC)(int,...);
typedef int (*MAPFUNCTION)(int [], INTFUNC, ...);
//------------------CALLBACK FUNCTION----------------
static int callbackfunction(int DatabaseRecord,int myArgument,int *MyResult){
if(DatabaseRecord < myArgument){
printf("mapfunction record:%d<%d -> result %d+%d=%d\n",DatabaseRecord,myArgument,*MyResult,DatabaseRecord,*MyResult+DatabaseRecord);
*MyResult+=DatabaseRecord;}
else{
printf("mapfunction record:%d<%d not true\n",DatabaseRecord,myArgument);
}
return 0; // keep looping
}
//------------------INVOCATION FUNCTION---------------
static int MapDatabase(int DataBase[], INTFUNC func, void* a1, void* a2, void* a3, void* a4, void* a5, void* a6, void* a7, void* a8, void* a9)
{
int cnt,end;
int ret = 0;
end = DataBase[0]+1;
for(cnt = 1;cnt<end;++cnt){
if(func(DataBase[cnt], a1, a2, a3, a4, a5, a6, a7, a8, a9)) {
ret = DataBase[cnt];
break;
}
}
return ret;
}
//------------------TEST----------------
void TestDataBase3(void)
{
int DataBase[20];
int cnt;
int RecordMatch;
int Result = 0;
DataBase[0] = 19;
for(cnt = 1;cnt<20;++cnt){
DataBase[cnt] = cnt;}
// here I do the cast to MAPFUNCTION and INTFUNC
RecordMatch = ((MAPFUNCTION)MapDatabase)(DataBase,(INTFUNC)callbackfunction,11,&Result);
printf("TestDataBase3 Result=%d\n",Result);
}
The same functionality can perfectly be written by using va_start/va_end. It might be the more official way of doing things, but I find it less user friendly. Either the callbackfunction needs to decode its arguments or you need to write a switch/case block inside the invocation function for every combination of arguments that the callback function can have. This means that you have to supply the format of the arguments (just like printf does) or you have to require that all arguments are the same and you just supply the number of arguments, but then you still have to write a case for each amount of arguments. Here is an example where the callback function decodes the arguments:
#include <stdio.h>
#include <stdarg.h>
//------------------CALLBACK FUNCTION----------------
static int callbackfunction(int DatabaseRecord,va_list vargs)
{
int myArgument = va_arg(vargs, int); // The callbackfunction is responsible for knowing the argument types
int *MyResult = va_arg(vargs, int*);
if(DatabaseRecord < myArgument){
printf("mapfunction record:%d<%d -> result %d+%d=%d\n",DatabaseRecord,myArgument,*MyResult,DatabaseRecord,*MyResult+DatabaseRecord);
*MyResult+=DatabaseRecord;}
else{
printf("mapfunction record:%d<%d not true\n",DatabaseRecord,myArgument);
}
return 0; // keep looping
}
//------------------INVOCATION FUNCTION---------------
static int MapDatabase(int DataBase[], int (*func)(int,va_list), int numargs, ...)
{
int cnt,end;
int ret = 0;
va_list vargs;
end = DataBase[0]+1;
for(cnt = 1;cnt<end;++cnt){
va_start( vargs, numargs ); // needs to be called from within the loop, because va_arg can't be reset
if(func(DataBase[cnt], vargs)) {
ret = DataBase[cnt];
break;
}
va_end( vargs ); // avoid memory leaks, call va_end
}
return ret;
}
//------------------TEST----------------
void TestDataBase4(void)
{
int DataBase[20];
int cnt;
int RecordMatch;
int Result = 0;
DataBase[0] = 19;
for(cnt = 1;cnt<20;++cnt){
DataBase[cnt] = cnt;}
RecordMatch = MapDatabase(DataBase,callbackfunction,2,11,&Result);
printf("TestDataBase4a Result=%d\n",Result);
Result = 0;
RecordMatch = MapDatabase(DataBase,callbackfunction,0,11,&Result); // As a hack: It even works if you don't supply the number of arguments.
printf("TestDataBase4b Result=%d\n",Result);
}
#Redef, if your compiler optimizes args into registers, it need not push them on the stack unless they are vargs. This means, in your first example, that callbackfunction will be expecting args in registers whilst the caller using INTFUNC (with a vargs decl) pushes them on the stack.
The result will be that the callback doesn't see the args.