Обратный вызов с переменным шаблоном для неоднозначных перегрузок - PullRequest
0 голосов
/ 10 апреля 2011

В C ++ 0X я хочу написать общие функции вызова / обратного вызова с использованием шаблонов с переменным числом аргументов. Первое препятствие: вызываемые являются членами-функциями. Все идет нормально. Второе препятствие: существует много перегруженных функций-членов с одинаковыми именами.

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

Хорошо, давайте углубимся в:

Class Foo
{
    void bar(int ID, int, int) { ... }
    void bar(int ID) { ... }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<typename ... Args>
    void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
    {
        refB(15, args...);
    }

    void yum()
    {
        sendBarToID_15(&Foo::bar, this, 17, 29); // want first version
    }
};

Однако я не могу скомпилировать вызов в yum (), потому что перегрузка препятствует разрешению шаблона. Согласно указанной статье, я должен явно создать объект функции

f = magic::make_function<help, me>(&Foo::bar)

и затем неторопливо позвоните sendBarToID_15(f, this, 17, 29).

  1. Как я могу сделать эту работу?

  2. Бонусные баллы за магию std :: bind, которая устраняет «this» в последней строке.

  3. Дополнительные бонусные баллы за 15 параметрических настроек.

Большое спасибо !!

Ответы [ 4 ]

2 голосов
/ 10 апреля 2011

Это то, что вы ищете?

#include <functional>
#include <iostream>

class Baz;

class Foo
{
    void bar(int ID, int, int) { std::cout << "here\n"; }
    void bar(int ID) { /*...*/ }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<int ID, typename ... Args>
    void sendBarToID(std::function<void(int, Args...)> refB, Args&& ... args)
    {
        refB(ID, std::forward<Args>(args)...);
    }

public:
    void yum()
    {
        using namespace std::placeholders;
        void (Foo::*mfp)(int, int, int) = &Foo::bar;
        sendBarToID<15>(std::function<void(int, int, int)>
            (std::bind(mfp, this, _1, _2, _3)), 17, 29); // want first version
    }
};

int main()
{
    Foo foo;
    foo.yum();
}
1 голос
/ 10 апреля 2011

Используйте лямбда-функцию - такого рода вещи больше не нужны.

class Foo
{
    void bar(int ID, int, int) { ... }
    void bar(int ID) { ... }
    void bar(int ID, double, float, void(Baz::*)()) const { /* jikes */ }

    template<typename ... Args>
    void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
    {
        refB(15, args...);
    }

    void yum()
    {
        sendBarToID_15([&, this](int i) {
            this->bar(i, 17, 29);
        });
    }
};
0 голосов
/ 10 апреля 2011

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

void sendBarToID_15(std::function<void(int)> f)
{
    f(15);
}

void yum()
{
    // No avoiding this in the presence of overloads
    void (Foo::*mfp)(int, int, int) = &Foo::bar;

    sendBarToID_15(std::bind(mfp, this, std::placeholder::_1, 17, 29));
}

Я надеялся, что решение с использованием шаблонов с переменным числом аргументов может каким-то образом упростить клиентский код, но сейчас я не понимаю, как оно может стать проще, чем это. Variadic #define макросы заботятся обо всем остальном.

Спасибо за вклад!

Обновление: Хорошо, вот что я наконец-то придумал, благодаря макросам препроцессора:

#include <functional>
#include <iostream>

class Baz;

class Foo
{
    void bar(int ID, const int &, int)
    { std::cout << "v1 called with ID " << ID << "\n"; }
    void bar(int ID)
    { std::cout << "v2 called with ID " << ID << "\n"; }
    void bar(int ID, double, float, void(Baz::*)()) const
    { std::cout << "v3 called with ID " << ID << "\n"; }

    void innocent(int ID, double)
    { std::cout << "innocent called with ID " << ID << "\n"; }

    void very_innocent(int ID, double) const
    { std::cout << "very innocent called with ID " << ID << "\n"; }

    template<int ID> void sendBarToID(std::function<void(int)> refB) { refB(ID); }
    template<int ID> void sendConstBarToID(std::function<void(int)> refB) const { refB(ID); }

#define MAKE_CALLBACK(f, ...) std::bind(&Foo::f, this, std::placeholders::_1, __VA_ARGS__)
#define MAKE_EXPLICIT_CALLBACK(g, ...) std::bind(g, this, std::placeholders::_1, __VA_ARGS__)

#define MAKE_SIGNED_CALLBACK(h, SIGNATURE, ...) MAKE_EXPLICIT_CALLBACK(static_cast<void (Foo::*)SIGNATURE>(&Foo::h), __VA_ARGS__)
#define MAKE_CONST_SIGNED_CALLBACK(h, SIGNATURE, ...) MAKE_EXPLICIT_CALLBACK(static_cast<void (Foo::*)SIGNATURE const>(&Foo::h), __VA_ARGS__)

public:
    void gobble()
    {
      double q = .5;
      int    n = 2875;
      void(Baz::*why)();

      sendBarToID<5>(MAKE_CALLBACK(innocent, q));
      sendConstBarToID<7>(MAKE_CALLBACK(very_innocent, q));
      // sendBarToID<11>(MAKE_SIGNED_CALLBACK(bar, (int))); // can't do, too much commas
      sendBarToID<13>(MAKE_SIGNED_CALLBACK(bar, (int, const int &, int), n, 1729));
      sendConstBarToID<17>(MAKE_CONST_SIGNED_CALLBACK(bar, (int, double, float, void(Baz::*)()), q, q, why));
    }

    void yum() const
    {
      double q = .5;
      int    n = 2875;
      void(Baz::*why)();

      sendConstBarToID<2>(MAKE_CALLBACK(very_innocent, q));
      // sendBarToID<-1>(MAKE_CALLBACK(innocent, q)); // Illegal in const function

      sendConstBarToID<3>(MAKE_CONST_SIGNED_CALLBACK(bar, (int, double, float, void(Baz::*)()), q, q, why));
    }
 };

int main()
{
    Foo foo;
    foo.yum();
    foo.gobble();
}

Есть одно неудобство: мне нужно определить две отдельные функции и макросы для постоянных и непостоянных функций-членов. Также я не могу обработать пустой список аргументов (Foo :: bar (int)).

0 голосов
/ 10 апреля 2011

Вы пытаетесь вызвать bar со следующими аргументами по порядку: 15, this, 17, 29.

Вы хотите: это, 15, 17, 29

template<typename ... Args>
void sendBarToID_15(std::function<void(int, Args...)> refB, Args ... args)
{
    refB(15, args...);
}

So & Foo :: bar не может быть std :: function

Если вы можете использовать лямбду, я бы использовал:

 void yum()
 {
    // EDITED: was "Foo* t"
    // (I don't remember of capture works with this, you may not need this)
    Foo* p = this;
    sendBarToID_15([p](int x, int y, int z){ p->bar(x, y, z); }, 17, 29);
 }

если вы не можете реализовать его с помощью вспомогательного класса:

class helper {
private:
   Foo* p;
public:
   [...]
   void operator(int x, int y, int z) {
      p->bar(x,y,z);
   }
}

или используя связывание:

// EDITED: wrong return type was used
void (Fred::*f)(char x, float y) = &Foo::bar;
sendBarToID_15(std::bind(f, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 17, 29);
...