С ++ препроцессорная конкатенация - PullRequest
0 голосов
/ 07 октября 2010

У меня есть сборка функции с указателями на функции. Я думаю, что это может быть быстрее, чтобы попытаться обменять эту функцию с макросом препроцессора. По крайней мере, я хотел бы попробовать макрос, чтобы я мог измерить, генерирует ли он более быстрый код.

Это более или менее так:

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);
// ...

Я думаю, что можно поменять это на что-то вроде:

#define DO_STUFF(item, getter, setter, k) do { \
int value = item ## -> ## getter ## (); \
//... \
item ## -> ## setter ## (newValue); \
} while(0);

но это дает мне такие ошибки, как:

ошибка: вставка ")" и "setA" не дает действительный токен предварительной обработки

Есть способ объединить имена функций и их объекты?

Ответы [ 3 ]

5 голосов
/ 07 октября 2010

Лучше использовать встроенные функции, чем макросы.Использование любого хорошего компилятора даст вам ту же эффективность, что и макрос, но с добавленной проверкой типов и отладочными символами, если они вам нужны.

3 голосов
/ 07 октября 2010

Вставка токенов означает «объединение двух токенов в один токен».

Вам этого не нужно.ptr_to_item->a() это не один токен.Предполагая, что ptr_to_item является именем переменной, оно равно 5: ptr_to_item, ->, a, (, ).

Ваш макрос должен быть просто:

#define DO_STUFF(item, getter, setter, k) do { \
    int value = (item)->getter(); \
    //... \
    (item)->setter(newValue); \
} while(0);

Кстати, для ненавистников макросов это позволяет избежать макросов, а также избежать использования функции указателя на член в качестве параметра функции.Это можно попробовать, если макрос работает быстрее, чем функция спрашивающего, поскольку вызов через указатель не является встроенным.Я не знаю, если / когда это будет иметь значение:

#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";
}

Потому что это есть.

Есть по крайней мере одна слабость.Вы не можете быть ссылочным типом в шаблоне.Но поскольку в коде, о котором идет речь, он фактически зафиксирован как int, параметр шаблона U может вообще не понадобиться, так что, надеюсь, это не слишком ограничивает.

2 голосов
/ 07 октября 2010

Указатели членов, как правило, плохо для эффективности.Они также плохо подходят для безопасности, потому что в системе типов C ++ есть лазейка, касающаяся доступности.

Вместо вашего текущего дизайна для doStuff, как ...

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);

..рассмотреть ...

int foo( int value )
{
    // .. Do some stuff 

     return newValue;
}

//...
item.setA( foo( item.a() ) );
item.setB( foo( item.b() ) );
item.setC( foo( item.c() ) );

Избавление от метода установки / получения для класса Item, вероятно, упростит ситуацию еще больше.В любом случае, я рекомендую попробовать изменить дизайн.Для этого имейте в виду ответственность экземпляра Item и какие знания ему необходимы для этого.

Приветствия & hth.,

- Alf

...