Как можно сделать функцию 'passthru' в C++ с помощью макросов или метапрограммирования?

Таким образом, я имею серию глобальных функций, говорю:

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 ().

7
задан Ryan 24 May 2010 в 19:43
поделиться

6 ответов

Вы можете использовать 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) \
    ); \
  }
10
ответ дан 6 December 2019 в 11:46
поделиться

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

#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)) для проверки вывода метафункций препроцессора.

2
ответ дан 6 December 2019 в 11:46
поделиться

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

#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
ответ дан 6 December 2019 в 11:46
поделиться

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

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

0
ответ дан 6 December 2019 в 11:46
поделиться

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

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

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

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

5
ответ дан 6 December 2019 в 11:46
поделиться

Идеальная пересылка опирается на ссылки rvalue. В блоге 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
ответ дан 6 December 2019 в 11:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: