C ++ функцыя-член паказальнік з рознымі аргументамі - ці гэта дрэнна ў любым выпадку?

Нягледзячы на ​​тое, я баюся, што ты раскажаш мне, што гэтая тэма была пакрыта некалькі разоў, я адважуся спытаць яго, так як я не быў у стане генераваць рашэнне. Магчыма, я проста шукаў няправільныя рэчы ...

Выкажам здагадку, што ў мяне ёсць функцыя, якая прымае «рэжым» ад некаторай знешняй функцыі. У залежнасці ад рэжыму, функцыя будзе выклікаць розныя функцыі-члены аднаго і таго ж аб'екта. Гэта добра працуе для мяне з функцыяй члена без якіх-небудзь аргументаў, але я не знайшоў, як распаўсюдзіць яго сябра з аргументамі. У рэальным дадатку, аргументы ня Int/паплаўка, а больш складаныя класы і выклік укладзены ў розных цыклах, таму я павінен быў бы паставіць ЗАЯВЫ перамыкач некалькі разоў, якія я лічу непрыгожа.

Пытанне A: Ці можна лёгка дадаць падтрымку функцый-членаў з аргументамі на аснове існуючай канструкцыі? Калі так, то як жа гэта зрабіць? Калі гэта магчыма без знешніх бібліятэк ...

Пытанне B: Гэта зусім няправільна/дрэнны падыход? Як бы я зрабіць гэта лепш?

Вялікі дзякуй за вашу дапамогу і тлумачэнні.

Крыс

Загаловак ўрывак:

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

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

касты вытрымка:

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
@ Бэн-Фойгт: Было б спрасціць мой праект, таму што ўнутры працэсу() функцыя член выклікаецца ў некалькіх месцах ўнутры алгарытму. У адваротным выпадку я б перайсці ў рэжым і іншыя аргументы для працэсу (), а затым адрозніваць кожны раз. Шукаю больш празрыстай канструкцыі ...
дададзена аўтар Chris, крыніца
Можа выкарыстоўваць шаблон?
дададзена аўтар Barmar, крыніца
Я не ведаю, як паказальнікі на функцыі-члены спрашчаюць вашу канструкцыю. Падобна на тое, ператвараючы алгарытм, які выкарыстоўвае аб'ект у функтор можа быць лепшым падыходам.
дададзена аўтар Ben Voigt, крыніца
Можа быць, станд :: прывязваць дапаможа тут?
дададзена аўтар BoBTFish, крыніца

7 адказы

Абкручванне алгарытму ў функторе правільнага падыход, і станд :: функцыя добрага функтор забяспечваецца стандартнай бібліятэкай.

Але выкарыстоўваючы павышэнне :: звязаць ці нават станд :: прывязваць , як гэта было прапанавана Томэк, сапраўды непрыгожа ИМО, і хутка выходзіць з-пад кантролю, калі звязванне некалькіх аргументаў.

Калі ў вас ёсць нядаўні кампілятар вы можаце выкарыстоўваць лямбда замест гэтага, што робіць прыклад Томэк выглядае як:

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

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

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

Ёсць некалькі знакаў, каб наладзіць лямбда, але сінтаксіс доступу члена вельмі натуральна, і гэта відавочна, як вы ўносіце змены.

Вядома, вы можаце зрабіць параметр спасылкі на аб'ект, калі вы сапраўды хочаце, але я аддаю перавагу паказальнікі тут.

2
дададзена
Выдатна, дзякуй за вашу дапамогу.
дададзена аўтар Chris, крыніца
Дзякуй за гэта вельмі элегантнае прапанова! Проста яшчэ адно пытанне: (Як) я магу зрабіць гэта, каб працаваць таксама з аргументамі паказальніка? Я атрымаў памылку «паказальнік" не улоўліваецца. Ці ёсць нешта накшталт станд :: вых() для паказальнікаў?
дададзена аўтар Chris, крыніца
@ Крыс: Калі вы хочаце перадаць аргумент, які з'яўляецца зменнай замест літаральнага канстанты, пералічыце зменную паміж [] . Такім чынам, лямбда будзе захоўваць сваё значэнне для наступнага выкарыстання падчас выкліку функцыі члена.
дададзена аўтар Ben Voigt, крыніца
@ Крыс: Я проста абнавіў свой ўзор кода з прыкладам.
дададзена аўтар Ben Voigt, крыніца

Паглядзіце на станд :: функцыі і станд :: цяжкае, яны, здаецца, падыходзяць ідэальна, што вам трэба.

EDIT:

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

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

павінна працаваць з скрынкі, наколькі я памятаю. Ці з'яўляецца гэта тое, што вам трэба?

2
дададзена
Я рады чуць BIND, таму што гэта была адна рэч, якую я думаў, але не быў упэўнены. На дадзены момант я змагаюся з праходжаннем толькі функцыі-члена без гэтага паказальніка, каб звязаць ... Я рэдагаваў гэтую праблему, каб праілюстраваць гэтае пытанне: я хачу, каб мець магчымасць прайсці быць, якая выкарыстоўваецца для-функцыі-члена да таго асобніка аб'екта.
дададзена аўтар Chris, крыніца
@Tomek: Гэта выглядае шматабяцальным, але я да гэтага часу не разумею, як апрацоўваць функцыі-члены з аргументамі. Скажам, я раблю прызначэнне станд :: функцыі ўнутры заяву перамыкача. Тады я павінен быў бы перадаць функцыю ў якасці аргументу «нікчэмнай працэсу ()». Я спрабаваў зрабіць гэта з дапамогай прысваення аўто f1 = станд :: Bind (& Object :: memberFnWithIntArg, 33); але тып f1 не тое ж самае, як е, так што я не можа апрацоўваць F, f1 і f2 з адной і той жа функцыяй працэсу ...
дададзена аўтар Chris, крыніца
@ Крыс: Вы можаце звязаць любой аргумент, а не толькі мэтавай аб'ект.
дададзена аўтар Ben Voigt, крыніца
Вам не трэба выкарыстоўваць mem_fn або падобнае тут?
дададзена аўтар Ben Voigt, крыніца
@ Крыс: Праверце свае змены. Калі ласка, звярніце ўвагу, я не ўпэўнены, што я атрымаў гэта права (я не памятаю дакладнае выкарыстанне станд :: цяжкага). Ключавое слова, якое вы, верагодна, шукае гэта «запаўняльнік».
дададзена аўтар Tomek, крыніца
@Ben: Я не ўпэўнены, не выкарыстоўвалі яго на працягу доўгага часу. Я лічу, станд :: функцыя і станд :: прывязваць павінны выціскаць, а не пашырыць, mem_fn. Ёсць некаторыя прыклады тут: en.cppreference.com/w/cpp/utility/ функцыянал/звязваем
дададзена аўтар Tomek, крыніца

Вы можаце выкарыстоўваць varadic шаблон функцыі:

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

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

Вось поўны прыклад:

#include 

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 
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
дададзена

Тое ж, як і іншыя адказы, але, каб паказаць метады членаў:

#include 
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 
    void process(void (Object::*func)(Args...),Args... args);
   //overload process
    template 
    bool process(bool (Object::*func)(Args...),Args... args);
};
template 
void  process( void (Object::*func)(Args...),class Object* obj,Args... args)
{

    (obj->*func)(args...);
}
template 
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
дададзена

Адзін са спосабаў я бачу вакол гэтага было б выкарыстоўваць зменную аргументы (у значнай ступені як 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
дададзена
Дзякуй за прапанову, але гэта рашэнне не здаецца, каб спрасціць сваю задачу ... гэта так жа, як у празрыстым як заяве пераключальніка.
дададзена аўтар Chris, крыніца
Хоць гэта можа быць зроблена, каб выканаць жаданы эфект, я б расці «праз мой труп» агляд.
дададзена аўтар Balog Pal, крыніца

Падобна на тое, packaged_task . Таксама праверце прапанову Томэк ст.

Хоць IRL я ісці наперад прасіць шмат пытанняў аб тым, чаму вам гэта трэба ў першую чаргу. Магчыма, ваша праца можа быць лепш пакрыта, выкарыстоўваючы станд :: будучы або іншы вышэйшы ўзровень аб'ект,

0
дададзена
Дзякуй за адказ. Можа быць, я проста не разумею, але я думаю, што packaged_task і будучыню дапамагаюць з праблемамі multihreading якіх гэтае пытанне не пра тое.
дададзена аўтар Chris, крыніца
будучыня можа быць запушчаны сінхранізацыі або асинхр - кропка гэтага можа быць вельмі блізка да таго, што вы хочаце, стварыць «заказ» і на больш познім этапе мець магчымасць атрымаць вынік. Што яна робіць паміж гэта проста цуд.
дададзена аўтар Balog Pal, крыніца

кожная функцыя (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
дададзена