C++ preprocessor concatenation
I have a function build with function pointers. I think it might be faster to try to exchange this function with pre processor macro. At least, I would like to try out the macro so I can measure if it generates faster code.
It's more or less like this:
typedef int (Item::*GetterPtr)(void)const;
typedef void (Item::*SetterPtr)(int);
void doStuff(Item* item, GetterPtr getter, SetterPtr setter, int k)
{
int value = (item->*getter)();
// .. Do some stuff
(item开发者_开发百科->*setter)(newValue);
}
And it's called like
// ...
doStuff(&item, &Item::a, &Item::setA, _a);
doStuff(&item, &Item::b, &Item::setB, _b);
doStuff(&item, &Item::c, &Item::setC, _c);
// ...
I think it might be possible to swap this with something like:
#define DO_STUFF(item, getter, setter, k) do { \
int value = item ## -> ## getter ## (); \
//... \
item ## -> ## setter ## (newValue); \
} while(0);
but it gives me errors like:
error: pasting ")" and "setA" does not give a valid preprocessing token
There's a way to concatenate the function names and it's object?
It is better to use inline functions than macros. Using any good compiler, this will give you the same efficiency as the macro, but with added type checking and debug symbols if you need them.
Token-pasting means "combining two tokens to form a single token".
You don't want that. ptr_to_item->a()
isn't one token. Assuming ptr_to_item
is a variable name, it's 5: ptr_to_item
, ->
, a
, (
, )
.
Your macro should just be:
#define DO_STUFF(item, getter, setter, k) do { \
int value = (item)->getter(); \
//... \
(item)->setter(newValue); \
} while(0);
By the way, for the macro haters, this avoids macros while also avoiding the use of a pointer-to-member-function as a function parameter. It could be tried if the macro is faster than the questioner's function due to the call through a pointer not being inlined. I don't know if/when it will make a difference:
#include <iostream>
struct A {
int f;
int foo() {return f;}
void setfoo(int a) { f = a; }
};
template <typename T, typename U, U (T::*GETTER)(), void (T::*SETTER)(U)>
void doit(T &obj, U k) {
U value = (obj.*GETTER)();
value += k;
(obj.*SETTER)(value);
}
int main() {
A a = {0};
std::cout << a.foo() << "\n";
doit<A,int,&A::foo, &A::setfoo>(a,1);
std::cout << a.foo() << "\n";
doit<A,int,&A::foo, &A::setfoo>(a,2);
std::cout << a.foo() << "\n";
}
Because it's there.
There's at least one weakness. U can't be a reference type in the template. But since it's effectively fixed as int
in the code in the question, the template parameter U may not be needed at all, so hopefully that isn't too limiting.
Member pointers are generally ungood for efficiency. They're also ungood for safety, because there's a loophole in the C++ type system regarding accessibility.
Instead of your current design for doStuff
like ...
typedef int (Item::*GetterPtr)(void)const;
typedef void (Item::*SetterPtr)(int);
void doStuff(Item* item, GetterPtr getter, SetterPtr setter, int k)
{
int value = (item->*getter)();
// .. Do some stuff
(item->*setter)(newValue);
}
//...
doStuff(&item, &Item::a, &Item::setA, _a);
doStuff(&item, &Item::b, &Item::setB, _b);
doStuff(&item, &Item::c, &Item::setC, _c);
... consider ...
int foo( int value )
{
// .. Do some stuff
return newValue;
}
//...
item.setA( foo( item.a() ) );
item.setB( foo( item.b() ) );
item.setC( foo( item.c() ) );
Getting rid of the setter/getter-design for the Item
class will probably simplify things even more. Anyway, I recommend that you try re-designing. For that, keep in mind the responsibilites of an Item
instance, and what knowledge it needs for that.
Cheers & hth.,
– Alf
精彩评论