开发者

C++ preprocessor concatenation

开发者 https://www.devze.com 2023-01-18 23:06 出处:网络
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

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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号