Andreas Micheler wrote:
Hi,
I have several long complex C macros in the math module of aUCBLogo like
#define _XFUNC_primitive(NAME)\
NodeP _##NAME(PairPP arg)\
{ ((ObjectP)(arg[1]))->var()->NAME( arg[2] );\
return Unbound;\
}
which are hard to debug, and now I thought about changing them into
templates. Is this possible in principle?
It depends on what the macros do. Templates can do some things that
macros can't and vice versa.
One thing that's going on here is that the macro call can specify a
name, which is inserted into the expansion. The user writes
_XFUNC_primitive(foo) and the macro writes a function called _foo,
which calls through this argument array through a function pointer
called foo.
(Let's not discuss the clueless intrusions into forbidden symbol
namespaces; since you didn't write this).
Templates cannot be parametrized on identifiers. They can't generate
identifiers, or insert identifiers into code. If you write a template
function called foo, all of its expansions will be various overloads of
a function called foo. Moreover, you can't pass a parameter into the
template which will specify the name of a function to be called from
the body.
What you can do is write it as a template class instead: a class which
overloads operator (). Then you can make instances of that class, and
give those instances names. The indirection upon the function pointer
can be done using a pointer-to-member.
To do that, we make the template parameter a pointer-to-member, which
points to a function pointer:
Simplified example:
#include <cstddef>
#include <iostream>
using namespace std;
struct operations {
size_t (*read)(void *, size_t);
size_t (*write)(void *, size_t);
};
template <size_t (* operations::*func)(void *, size_t)>
class wrapper
{
public:
size_t operator()(operations *op, void *ptr, size_t size)
{
return (op->*func)(ptr, size);
}
};
size_t read_func(void *buf, size_t size)
{
cout << "read_func called on " << buf << " of size " << size <<
endl;
}
size_t write_func(void *buf, size_t size)
{
cout << "write_func called on " << buf << " of size " << size <<
endl;
}
int main()
{
unsigned char buf[256];
// table of operations
operations ops = { read_func, write_func };
// use the template to generate wrapper "functions" that call
indirect thorugh
// the operations structure
wrapper<&operations::read> read_wrapper;
wrapper<&operations::write> write_wrapper;
// call the read wrapper, on the given operations structure.
Control goes
// through read_wrapper object's operator (), which calls ops.read,
// ending up in read_func.
read_wrapper(&ops, buf, sizeof buf);
return 0;
}