c ++ создает имя для таблицы функций, функции имеют разные сигнатуры - PullRequest
0 голосов
/ 15 ноября 2018

Я хочу знать, возможно ли создать имя для таблицы функций в c ++, что-то вроде map<string, function handle>.Но

  1. эти функции имеют разные подписи.Я могу предположить, что они имеют тот же тип возврата, что и void.

  2. Я думал определить что-то вроде,

    struct ftable { std::string name; void (void* pfun)(); // this is only for one function signature };

Но как заставить это работать для разных типовfunction?

Я задал связанный вопрос здесь .В этом вопросе я пытаюсь сохранить функции в некотором контейнере, но я понимаю, что не могу сохранить функцию с заполнителем (см. Следующий код).Это возможно с C ++?Спасибо!

template <typename F, typename ... Args>
std::function<void()> MapFun (F const & f, Args const & ... args)
    { return [=]{ f(args...); }; }

void userFun1 (int i1, int i2)
{ std::cout << "uf1, " << i1 << ", " << i2 << std::endl; }

int main ()
{
   auto l1 = MapFun(userFun1, 1, 2);
   std::unordered_map<std::string, std::function<void()>> func_map;
   func_map["userFun1"] = std::bind(l1); // this is okay;
   //func_map["userFun1"] = std::bind(userFun1, std::placeholders::_1, std::placeholders::_2); // this is wrong;
   //auto lll1 = MapFun(userFun1, std::placeholders::_1,std::placeholders::_2); // also wrong.
}

ОБНОВЛЕНИЕ : То, что я хочу, похоже на разработку плагинов.У меня есть сервер и клиент.Я могу написать свою функцию на клиенте и построить ее как разделяемую библиотеку, а затем отправить эту разделяемую библиотеку на сервер, а также на сервер оповещения о сигналах для ее загрузки.Я надеюсь, что серверная программа может загружать функции с разными сигнатурами из общей библиотеки без перезапуска или перестройки.Возможно ли это, или я должен исправить подпись и ограничить все общие функции с этим?

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Возможно, но в какой-то момент вам потребуется знать типы возвращаемых данных и параметров.

Вы можете использовать класс стирания типа, чтобы скрыть типы возвращаемых данных / параметров, а затем сохранить класс стирания типа. Достаточно наивной реализации, подобной этой,

#include <iostream>
#include <functional>
#include <unordered_map>

class MyLambda {    

    public:

        MyLambda() = default;

        virtual ~MyLambda() = default;

};

template <typename T>
class HiddenLambda : public MyLambda {
    static_assert(std::integral_constant<T, false>::value, "Template parameter needs to be of function type.");
};

template <typename Ret, typename... Args>
class HiddenLambda<Ret(Args...)> : public MyLambda {

        public:

            HiddenLambda(std::function<Ret(Args...)> _fun) :  fun_(_fun) { }

            Ret operator() (Args... args) {return fun_(args...);}

        private:

        std::function<Ret(Args...)> fun_;
};

int main() {

    std::unordered_map<std::string, std::shared_ptr<MyLambda>> my_lambdas;

    my_lambdas.insert(std::make_pair("fun1", std::shared_ptr<MyLambda>( 
        new HiddenLambda<size_t(std::string)>(
            [](std::string s) { return s.size(); } // <- lambda you want to store
            )
        )
    ));

    my_lambdas.insert(std::make_pair("fun2", std::shared_ptr<MyLambda>( 
        new HiddenLambda<int(int)>(
            [](int x) { return x * 5; } // <- lambda you want to store
            )
        )
    ));

    auto it = my_lambdas.find("fun1");

    /* Get the function we want! */
    std::shared_ptr<MyLambda> a_lam = it->second;

    /* Need to know the types to actually use it though */
    HiddenLambda<size_t(std::string)>& actual_lam = dynamic_cast<HiddenLambda<size_t(std::string)>&>(*a_lam);

    std::cout << actual_lam("how long is this string?") << "\n";
}

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

Я думаю, что проблема, которую вы пытаетесь решить, возможно, имеет более простое решение. Если бы вы могли дать более подробную информацию, возможно, мы могли бы помочь?

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

Более подходящий пример для предоставленного вами кода ...

/* include above classes and includes */
void myfunc(int x, int y) {
    std::cout << "uf1, " << x << ", " << y << std::endl;
}

int main() {
    std::unordered_map<std::string, std::shared_ptr<MyLambda>> func_map;

    func_map["userFun1"] = std::shared_ptr<MyLambda>(
        new HiddenLambda<void(int, int)>( &myfunc ) // <- no args binded, notice the type = void(int,int)
    ); 

    func_map["userFun2"] = std::shared_ptr<MyLambda>( 
        new HiddenLambda<void(int)>( std::bind(myfunc, std::placeholders::_1,  5) ) // <- one args binded, notice the type = void(int)
    ); 

    func_map["userFun3"] = std::shared_ptr<MyLambda>(
      new HiddenLambda<void()>(  std::bind(myfunc, 1, 2))  // <- two args binded, notice the type = void()
     );

    /* we still need to know the type though,it will be either void(int, int), void(int) or void() */
    HiddenLambda<void(int)>& actual_lam = dynamic_cast<HiddenLambda<void(int)>&>(*func_map["userFun2"]);

    actual_lam(4);

}

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

Это скорее догадка, чем что-либо еще. Я не уверен, стоит ли вам это делать (определенно не вне каких-то интересных экспериментов) или это будет работать. Вот возможный способ, если количество разных аргументов для разных функций известно и конечно. Это потребовало бы техники под названием «Вставка библиотеки», о которой я не очень разбираюсь.

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

enum Types { kVOID, kINT_INT }; // <- verbosely define all the possible ones you would use 

std::pair<Types, std::shared_ptr<MyLambda>> Factory(){
    return 
        {
          kVOID, /* <- some sensible default */
            std::shared_ptr<MyLambda>(
               new HiddenLambda<void()>( []{} ) 
            )
        }; 
}

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

std::pair<Types, std::shared_ptr<MyLambda>> Factory(){
    return
     {
        kVOID_INT_INT,
        std::shared_ptr<MyLambda>(
        new HiddenLambda<void(int, int)>( [](int x, int y){ std::cout << (x + y);} ) 
        )
   };
}

Тогда основной метод будет выглядеть так:

int main() {

    std::unordered_map<std::string, std::pair<Types, std::shared_ptr<MyLambda>>> func_map;

    func_map.insert({"fun1", Factory()});

    auto it = func_map.find("fun1");

    /* always need to be able to deduce they type */
    if (it->second.first == kVOID) {

        CallHidden(*it->second.second); 
    } 

    else if (it->second.first == kINT_INT) { 

        CallHidden<int, int>(*it->second.second, 3, 4); 

    } else {

        /* other ones you have statically typed */

    } 
}
0 голосов
/ 15 ноября 2018

Если во время создания объекта std::function<void()> вы знаете сигнатуру функции, вы можете сделать это, как показано ниже.

#include <iostream>
#include <functional>
#include <unordered_map>

void userFun1 (int i1, int i2)
{ 
    std::cout << "uf1, " << i1 << ", " << i2 << std::endl; 
}


int main()
{
    std::unordered_map<std::string, std::function<void()>> func_map;
    func_map["userFun1"] = std::bind(userFun1,1,2);
    func_map["userFun1"]();
    int i1{2},i2{3};
    func_map["userFun1.1"] = std::bind(userFun1,std::ref(i1),std::ref(i2));
    func_map["userFun1.1"]();
    i1 = 3;
    i2 = 4;
    func_map["userFun1.1"]();
    return 0;
}

Живой код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...