Как объявить std::map с перечислением в качестве ключа и функциями с различными сигнатурами в качестве значений?

Полагаю, это очень простой вопрос для продвинутых программистов на C++, но я не один, поэтому:

Используя С++ 11, какой элегантный способ реализовать std::map, который использует enum в качестве ключа и принимает математические функции с различными сигнатурами в качестве значений:

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

enum class FUNCS
    {
         DOUBLE_FUNC1, DOUBLE_FUNC2, INT_FUNC3, INT_FUNC4
    };

Некоторые функции:

double f1( int a, int b, double d);
double f2( int a, int b, int c, int d);
int f3( int a, double d, int c, double e);
int f4( int a, double d, int c);

Функциональность, которую я ищу, - это std::map, которая работает следующим образом:

Инициализируйте карту:

mMAP[FUNCS::DOUBLE_FUNC1]=f1;
mMAP[FUNCS::DOUBLE_FUNC2]=f2;
mMAP[FUNCS::INT_FUNC3]=f3;
mMAP[FUNCS::INT_FUNC4]=f4;

Используйте карту:

mMAP[FUNCS::DOUBLE_FUNC1](a,b,d);
mMAP[FUNCS::INT_FUNC3](a,d,c,e);

Компромисс, предполагающий некоторое приведение типов, может использовать функции с разными аргументами, которые все возвращают double:

double f1( int a, int b, double d);
double f2( int a, int b, int c, int d);
double f3( int a, double d, int c, double e);
double f4( int a, double d, int c);

Как я могу объявить карту, которая даст мне эту функциональность? Я понимаю, что я, вероятно, не могу напрямую сопоставить эти функции с этими ключами - мне понадобится какая-то абстракция/косвенность для достижения этой цели, и я считаю, что это можно сделать с помощью функций/шаблонов с переменным числом аргументов, см. Аргументы Variadic и Пакет параметров, но я действительно не понимаю, как заставить это работать.

Тем временем я реализовал неуклюжее решение, используя тип указателя функции, который принимает std::tuple, содержащий ряд значений всех возможных типов, в качестве аргумента со ссылками на результаты, а затем в каждой отображаемой функции я использую соответствующие члены кортежа. УЖАСНО!

Моей целью здесь является отправка данных из этих различных функций в библиотеку диаграмм, которая генерирует диаграммы, используя данные типа boost::any. Конечный автомат с графическим интерфейсом будет вызывать соответствующую функцию через карту на основе значения перечисления, представленного в конечном автомате, и создайте диаграмму на основе данных этой функции.


person Vector    schedule 09.12.2014    source источник
comment
Соответствует ли каждое значение FUNCS одному уникальному типу функции?   -  person T.C.    schedule 09.12.2014
comment
@Т.С. - Нет. Если бы были, то и вопроса бы не было! Пожалуйста, прочитайте. :)   -  person Vector    schedule 09.12.2014
comment
Другими словами, вы также хотите иметь возможность писать mMAP[FUNCS::DOUBLE_FUNC1] = f2;?   -  person T.C.    schedule 09.12.2014
comment
@Т.С. - в принципе да, хотя не в этом дело. Суть в том, чтобы иметь единый, лаконичный, симметричный интерфейс для отображения этих различных функций без всякого рода условной логики. Пожалуйста, внимательно прочитайте вопрос - я думаю, он достаточно ясен. Поправьте меня если я ошибаюсь. Извиняюсь.   -  person Vector    schedule 09.12.2014
comment
Как программисту или компилятору узнать, какую сигнатуру аргумента использовать?   -  person Unapiedra    schedule 09.12.2014
comment
@Unapiedra - это то, что делает карта.   -  person Vector    schedule 09.12.2014
comment
Голосующий против - объясните пожалуйста. Необъяснимые отрицательные голоса не очень конструктивны.   -  person Vector    schedule 09.12.2014
comment
@Vector, нет, карта ничего не говорит о подписи. По крайней мере, не так, как вы это представили на данный момент. Вы создали перечисление, но вам потребуется дополнительное сопоставление перечисления с подписью.   -  person Unapiedra    schedule 09.12.2014
comment
@Unapiedra - понял. Я оговорился - дело в том, что должна быть какая-то абстракция, не зависящая от параметров, которую карта может использовать соответствующим образом.   -  person Vector    schedule 09.12.2014
comment
Если ваша единственная проблема заключается в хранении данных на карте, почему бы просто не сохранить их в boost::any?   -  person DanielKO    schedule 10.12.2014
comment
@DanielKO - мне не нравится использовать boost::any в моем коде.   -  person Vector    schedule 10.12.2014
comment
boost::any гарантирует, что вы вернете его к правильному типу. Похоже, вы разрабатываете что-то настолько абстрактное, что оно должно отвечать только за передачу данных между производителем и потребителем. Это идеальная работа для boost::any.   -  person DanielKO    schedule 12.12.2014
comment
@DanielKO - Понятно. Я думал, что это работает просто как вариантный тип — возможно, это то, на что стоит обратить внимание.   -  person Vector    schedule 14.12.2014


Ответы (2)


C++ — это статически типизированный язык.

Единственный способ сделать это — использовать общую подпись для всех функций, например:

struct F1Data { double result; int a; int b; double d; };
struct F2Data { double result; int a, b, c, d; };
struct F3Data { int result; int a, b; double c; };
struct F4Data { int result; double a, b; int c; };

void f1(void* data);
void f2(void* data);
void f3(void* data);
void f4(void* data);

Затем внутри каждой функции вы можете привести data к нужному типу и работать со значениями оттуда.

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

person StilesCrisis    schedule 09.12.2014
comment
Как насчет аргументов Variadic и/или < a href="http://en.cppreference.com/w/cpp/language/parameter_pack" rel="nofollow noreferrer">Пакет параметров? - person Vector; 10.12.2014
comment
Аргументы с переменным числом аргументов сделают это, но они намного менее ясны, чем мое предложение. Пакеты параметров повлияют на тип функции, а также с ними будет сложнее работать. - person StilesCrisis; 10.12.2014
comment
Я, вероятно, просто собираюсь использовать переключатель, но, учитывая все обстоятельства, это, вероятно, самый простой и чистый способ выполнить то, что я ищу, поэтому я принимаю этот ответ. - person Vector; 10.12.2014
comment
Спасибо! Я согласен, иди с выключателем. Это более естественно. - person StilesCrisis; 10.12.2014

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

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

person BЈовић    schedule 09.12.2014
comment
Это можно сделать только с помощью компромиссов, которые объединяют все типы аргументов в один гибридный тип. C++ — это статически типизированный язык. - person StilesCrisis; 09.12.2014
comment
Я не уверен, что это невозможно. См. правку: аргументы Variadic и Пакет параметров - person Vector; 10.12.2014
comment
@Vector Вам лучше перепроектировать, чем пытаться использовать аргументы с переменным числом аргументов. На самом деле, как сказано здесь, вы можете использовать стирание типов, но тогда вам нужно поместить возвращаемое значение в структуру. - person BЈовић; 10.12.2014
comment
@BЈовић - никогда не работал с вариативными аргументами, и они не выглядят красиво; кастинг void* - это просто не лучший вариант дизайна IMO, хотя им было бы проще управлять, чем неуклюжим подходом tuple, который очень уродлив. Я начинаю думать, что должен просто забыть карту и использовать switch . - person Vector; 10.12.2014
comment
Да, используйте переключатель. Если вы можете избежать этой битвы, то сделайте это. - person StilesCrisis; 10.12.2014