Таким образом, я имею серию глобальных функций, говорю:
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 ().
Вы можете использовать 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) \
); \
}
Немного другой синтаксис, но ...
#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)) для проверки вывода метафункций препроцессора.
Вы можете использовать пространство имен
, если не хотите иметь дело с классами, например 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__)
...
Моя первоначальная мысль, и это, вероятно, не сработает, иначе другие заявили бы, состоит в том, чтобы объединить все ваши базовые функции в класс как виртуальные. Затем запишите улучшения функциональности в унаследованные классы и запустите их. Это не оболочка макросов, но вы всегда можете вызывать глобальные функции в виртуальных классах.
С помощью некоторых уловок сборки вы, вероятно, сможете сделать именно то, что хотите, но более чем вероятно потеряете переносимость. Интересный вопрос, и я хочу услышать и другие ответы.
При 40 с лишним функциях вы можете набрать обертки вручную за час. Компилятор проверит правильность результата. Предположим, что для каждой новой функции, требующей переноса, будут добавляться 2 минуты, а для изменения подписи - еще 1 минута.
Как указано и без упоминания частых обновлений или изменений, не похоже, что эта проблема требует хитрого решения.
Итак, я рекомендую не усложнять: делайте это вручную. Скопируйте прототипы в исходный файл, затем используйте макросы клавиатуры (emacs / Visual Studio / vim), чтобы исправить ситуацию, и / или несколько проходов поиска и замены, генерируя один набор определений и один набор объявлений. Вырезать объявления, вставить в заголовок. Введите определения для непроходных функций. Это не принесет вам никаких наград, но скоро все закончится.
Никаких дополнительных зависимостей, никаких новых инструментов сборки, хорошо работает с просмотром кода / тегами / intellisense / и т. Д., Хорошо работает с любым отладчиком и без специального синтаксиса / современных функций / шаблонов / и т. Д., Так что любой может понять результат . (Это правда, что никто не будет впечатлен, но это будет хорошее впечатление.)
Идеальная пересылка опирается на ссылки 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.