Как создать функцию «passthru» в C ++, используя макросы или метапрограммирование? - PullRequest
7 голосов
/ 24 мая 2010

Итак, у меня есть ряд глобальных функций, скажем:

foo_f1(int a, int b, char *c);
foo_f2(int a);
foo_f3(char *a);

Я хочу создать оболочку C ++ вокруг них, что-то вроде:

MyFoo::f1(int a, int b, char* c); 
MyFoo::f2(int a);
MyFoo::f3(char* a);

Там около 40 функций, таких какэто 35 из них, которые я просто хочу передать глобальной функции, остальные 5, с которыми я хочу сделать что-то другое.

В идеале реализация MyFoo.cpp должна выглядеть примерно так:

PASSTHRU( f1, (int a, int b, char *c) );
PASSTHRU( f2, (int a) );

MyFoo::f3(char *a)
{
   //do my own thing here
}

Но у меня возникают проблемы с поиском элегантного способа создания макроса PASSTHRU, описанного выше.

Что мне действительно нужно, так это что-то вроде мифического X getArgs () ниже:

MyFoo::f1(int a, int b, char *c)
{
  X args = getArgs();
  args++; //skip past implicit this..
  ::f1(args);  //pass args to global function 
}

Но если не вдаваться в сборку, я не могу найти хорошую реализацию getArgs ().

Ответы [ 6 ]

10 голосов
/ 25 мая 2010

Вы можете использовать Boost.Preprocessor , чтобы разрешить следующее:

struct X {
    PASSTHRU(foo, void, (int)(char))
};

... расширить до:

struct X {
    void foo ( int arg0 , char arg1 ) { return ::foo ( arg0 , arg1 ); }
};

... используя эти макросы:

#define DO_MAKE_ARGS(r, data, i, type) \
  BOOST_PP_COMMA_IF(i) type arg##i

#define PASSTHRU(name, ret, args) \
  ret name ( \
    BOOST_PP_SEQ_FOR_EACH_I(DO_MAKE_ARGS, _, args) \
  ) { \
    return ::name ( \
      BOOST_PP_ENUM_PARAMS(BOOST_PP_SEQ_SIZE(args), arg) \
    ); \
  }
5 голосов
/ 25 мая 2010

При 40 с лишним функциях вы можете набирать обертки вручную за час.Компилятор проверит правильность результата.Предположим, что для каждой новой функции, требующей переноса, предусмотрены дополнительные 2 минуты и дополнительная 1 минута для изменения подписи.

Как указано, без упоминания о частых обновлениях или изменениях, это не звучит такпроблема требует хитрого решения.

Итак, я рекомендую сделать это простым: делайте это вручную.Скопируйте прототипы в исходный файл, затем используйте макросы клавиатуры (emacs / Visual Studio / vim), чтобы исправить ситуацию и / или несколько проходов поиска и замены, генерируя один набор определений и один набор объявлений.Разрезать декларации, вставить в заголовок.Заполните определения для непроходных функций.Это не принесет вам никаких наград, но скоро закончится.

Никаких дополнительных зависимостей, никаких новых инструментов сборки, хорошо работает с просмотром кода / tags / intellisense / etc., Хорошо работает с любымотладчик, и нет специального синтаксиса / современные функции / шаблоны / и т. д., так что любой может понять результат.(Это правда, что никто не будет впечатлен - но это будет хорошим впечатлением.)

2 голосов
/ 25 мая 2010

Немного другой синтаксис, но ...

#include <boost/preprocessor.hpp>
#include <iostream>

void f1(int x, int y, char* z) { std::cout << "::f1(int,int,char*)\n"; }

#define GENERATE_ARG(z,n,unused) BOOST_PP_CAT(arg,n)
#define GET_ARGS(n)  BOOST_PP_ENUM(n, GENERATE_ARG, ~)

#define GENERATE_PARAM(z,n,seq) BOOST_PP_SEQ_ELEM(n,seq) GENERATE_ARG(z,n,~)

#define GENERATE_PARAMS(seq) BOOST_PP_ENUM( BOOST_PP_SEQ_SIZE(seq), GENERATE_PARAM, seq )

#define PASSTHROUGH(Classname, Function, ArgTypeSeq) \
  void Classname::Function( GENERATE_PARAMS(ArgTypeSeq) ) \
{ \
  ::Function( GET_ARGS( BOOST_PP_SEQ_SIZE(ArgTypeSeq) ) ); \
}

struct test
{
  void f1(int,int,char*);
};

PASSTHROUGH(test,f1,(int)(int)(char*))

int main()
{
  test().f1(5,5,0);

  std::cin.get();
}

Вы могли бы получить что-то ближе к своему, если бы использовали кортежи, но вам нужно было бы указать количество аргументов для базовой функции (вы не можетеполучить размер из кортежа).Вроде как так:

PASSTHROUGH(test,f1,3,(int,int,char*))

Это то, что вы ищете?Я знал, что это можно сделать;Потребовалось около получаса, чтобы решить.Вы, кажется, ожидаете, что есть неявное «это», от которого нужно избавиться, но я не понимаю, почему ... поэтому, возможно, я неправильно понимаю проблему.Во всяком случае, это позволит вам быстро выполнять «проходные» функции-члены по умолчанию, которые относятся к какой-то глобальной функции.Вам понадобится DECPASSTHROUGH для объявления класса, если вы хотите пропустить необходимость объявить их все ... или вы можете изменить это, чтобы сделать встроенные функции.

Подсказка: используйте BOOST_PP_STRINGIZE ((XX)) для проверкивывод метафункций препроцессора.

0 голосов
/ 25 мая 2010

Идеальная пересылка зависит от ссылок на значения.STL имеет запись в блоге на http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx, и вы захотите выбрать компилятор, который поддерживает эту функцию, чтобы использовать этот подход.Он обсуждает Visual C ++ 2010.

0 голосов
/ 25 мая 2010

Вы можете использовать namespace, если вы не хотите иметь дело с классными вещами, такими как this.Вы также можете использовать static методы члена в классе, но я думаю, что людям это больше не нравится.

#ifndef __cplusplus
#define PASSTHRU(type, prefix, func, args)  type prefix##_##func args
#else
#define PASSTHRU(type, prefix, func, args)  type prefix::func args
#endif

Или

#ifndef __cplusplus
#define PASSTHRU(type, prefix, func, ...)  type prefix##_##func(__VA_ARGS__)
...
0 голосов
/ 24 мая 2010

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

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

...