Указатель на функцию-член C ++ с разными аргументами — или это все равно плохо?

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

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

Вопрос A: Можно ли легко добавить поддержку функций-членов с аргументами, основанными на существующем дизайне? Если да, то как это сделать? Если возможно без внешних библиотек …

Вопрос B: Это совершенно неправильный / плохой подход? Как бы я сделал это лучше?

Большое спасибо за вашу помощь и объяснения.

Крис

выдержка из заголовка:

typedef void (Object::*memberFunction)();

class Object
{
void memberFnNoArg();
void memberFnWithIntArg(int arg);
void memberFnWithFloatArg(float arg);
}

выдержка cpp:

void function()
{
int mode = getModeFromSomewhere();

int intArg = 33;
float floatArg = 66.6;

switch(mode)
{
case 1:
process(&Object::memberFnNoArg);
break;
case 2:
process(&Object::memberFnWithIntArg, ???); // how can I pass arg?
break;
case 3:
process(&Object::memberFnWithFlaotArg, ???); // how can I pass arg?
break;
default:
// do nothing;
}

}

void process(Object::memberFunction func)
{
Object object;
// loops, called several times, ...
(object.*func)(); // how do I handle different arguments?
}

3

Решение

Обертывание алгоритма в функторе является правильным подходом, и std::function хороший функтор, предоставляемый стандартной библиотекой

Но используя boost::bind или даже std::bindКак предположил Томек, это действительно уродливое ИМО, которое быстро выходит из-под контроля при связывании нескольких аргументов.

Если у вас есть недавний компилятор, вы можете вместо этого использовать лямбду, что делает пример Томека похожим на:

std::function<void(Object*)> f  =
[](Object* const that){ that->memberFnNoArg(); };

int int_value = 22;
std::function<void(Object*)> f2 =
[int_value](Object* const that){ that->memberFnIntArg(int_value); };

Object o;
f(&o);
f2(&o);

Есть несколько символов для настройки лямбды, но синтаксис доступа к элементу является чрезвычайно естественным, и очевидно, как вы вносите изменения.

Конечно, вы можете сделать параметр ссылкой на объект, если вы действительно хотите, но я предпочитаю указатели здесь.

2

Другие решения

Взгляните на std :: function и std :: bind, они, кажется, идеально соответствуют тому, что вам нужно.

РЕДАКТИРОВАТЬ:

std::function<void(Object &)> f = &Object::memberFnNoArg;
std::function<void(Object &)> f2 = std::bind(&Object::memberFnWithIntArg, _1, 22);

Object o;
f(o);
f2(o);

должен работать из коробки, насколько я помню.
Это то, что тебе надо?

2

Вы можете использовать функцию varadic template:

template <typename... Args>
void process(void (Object::*func)(Args...),Args... args)
{
Object object;

// loops, called several times, ...
(object.*func)(args...);
}

Вот полный пример:

#include <iostream>

struct Object
{
void memberFnNoArg()
{
std::cout << "Object::memberFnNoArg()\n";
}

void memberFnWithIntArg(int arg)
{
std::cout << "Object::memberFnWithIntArg(" << arg << ")\n";
}

void memberFnWithFloatArg(float arg)
{
std::cout << "Object::memberFnWithFloatArg(" << arg << ")\n";
}
};

template <typename... Args>
void process(void (Object::*func)(Args...),Args... args)
{
Object object;

// loops, called several times, ...
(object.*func)(args...);
}

int main()
{
process(&Object::memberFnNoArg);
process(&Object::memberFnWithIntArg,5);
process(&Object::memberFnWithFloatArg,2.7F);
return 0;
}
1

Один из способов обойти это — использовать переменные аргументы (почти как printf, это делает sprintf). (Или, может быть, с библиотеками stdc, передавая список разных типов.)

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

Ниже приведен простой (не членский) пример того, как подобрать переменные аргументы (функции-члены по существу будут работать одинаково). Увидеть stdarg.h.

typedef void (*var_function)(int typearg, ...);

void print_arg(int typearg, ...)
{
va_list ap;
int i;

va_start(ap, typearg);

if (typearg==1) { // int
int i= va_arg(ap, int);
printf("%d ", i);
}
else
if (typearg==2) { // float
float f= va_arg(ap, float);
printf("%f ", f);
}
else
if (typearg==3) { // char *
char *s= va_arg(ap, char *);
printf("%s ", s);
}

....

va_end(ap);
}

// calling function with different types
int main()
{
print_arg(1, 999);
print_arg(2, 3.1415926);
print_arg(3, "Hello");
....
process(print_arg, 3, "via pointer);
0

Звучит как packaged_task. Также ознакомьтесь с предложением Томека.

Хотя IRL я бы продолжал задавать много вопросов о том, зачем вам это нужно в первую очередь. Возможно, ваша работа может быть лучше освещена с помощью std::future или другой объект более высокого уровня,

0

Разве каждая функция (memberFn **) не может быть членом класса аргументов?

class BaseArg
{
virtual void Fn() = 0;
};

class IntArg : public BaseArg
{
void Fn();
};

class FloatArg : public BaseArg
{
void Fn();
};void function()
{
int mode = getModeFromSomewhere();
BaseArg* pArg;

if ( mode ... ){
pArg = new IntArg( 33 );
}
else {
pArg = new FloatArg( 66.6 );
}

pArg->Fn();  // Call the right function without a switch
// and without knowing the arguments

}
0

То же, что и в других ответах, но для показа методов-членов:

#include <iostream>
class Object
{
public:
void memberFnNoArg()
{
std::cout << "Object::memberFnNoArg()\n";
}

void memberFnWithIntArg(int arg)
{
std::cout << "Object::memberFnWithIntArg(" << arg << ")\n";
}

void memberFnWithFloatArg(float arg)
{
std::cout << "Object::memberFnWithFloatArg(" << arg << ")\n";
}
bool memberFnWithBoolReturn(int)
{
return true;
}
template <typename... Args>
void process(void (Object::*func)(Args...),Args... args);
// overload process
template <typename... Args>
bool process(bool (Object::*func)(Args...),Args... args);
};
template <typename... Args>
void  process( void (Object::*func)(Args...),class Object* obj,Args... args)
{

(obj->*func)(args...);
}
template <typename... Args>
bool  process( bool (Object::*func)(Args...),class Object* obj,Args... args)
{
return ((obj->*func)(args...)) ;

}
int main()
{
Object object;
process(&Object::memberFnNoArg,&object);
process(&Object::memberFnWithIntArg,&object,5);
process(&Object::memberFnWithFloatArg,&object,2.7F);
// overloaded process
printf("%d\n",process(&Object::memberFnWithBoolReturn,&object,1));

return 0;
}
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector