Как да декларирам std::map с enum като ключ и функции с различни подписи като стойности?

Предполагам, че това е много прост въпрос за напреднали C++ програмисти, но аз не съм такъв, така че:

Използвайки 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);

Как мога да декларирам карта, която ще ми даде тази функционалност? Разбирам, че вероятно не мога да съпоставя тези функции директно с тези клавиши - ще ми трябва някаква абстракция/индиректност, за да постигна тази цел и вярвам, че с помощта на различни функции/шаблони това може да бъде постигнато, вижте Разнообразни аргументи и Пакет с параметри, но наистина не съм наясно как да накарам това да работи.

Междувременно имплементирах неудобно решение, използвайки тип указател на функция, който приема std::tuple, съдържащ серия от стойности от всички възможни типове като аргумент с препратки към резултатите, и след това във всяка картографирана функция използвам подходящите членове на кортежа. ГРОЗНО!

Целта ми тук е да изпратя данни от тези различни функции към библиотека с диаграми, която генерира диаграми, използвайки данни от тип boost::any GUI управлявана машина за състояние ще извика подходящата функция чрез картата въз основа на стойността enum, представена в машината за състояние, и генерирайте диаграма въз основа на данни от тази функция.


person Vector    schedule 09.12.2014    source източник
comment
Всяка стойност FUNCS отговаря ли на един единствен, уникален тип функция?   -  person T.C.    schedule 09.12.2014
comment
@T.C. - Не. Ако бяха, нямаше да има въпрос! Моля Прочети. :)   -  person Vector    schedule 09.12.2014
comment
С други думи, искате ли също да можете да пишете mMAP[FUNCS::DOUBLE_FUNC1] = f2;?   -  person T.C.    schedule 09.12.2014
comment
@T.C. - по принцип да, но не това е важното. Въпросът е да има единен, кратък, симетричен интерфейс за картографиране на тези различни функции без всякаква условна логика. Моля, прочетете внимателно въпроса - вярвам, че е съвсем ясен. Поправете ме, ако греша. съжалявам   -  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, не картата не казва нищо за подписа. Поне не както го представяте в момента. Създали сте enum, но ще ви трябва допълнително съпоставяне на enum към подпис.   -  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
Какво ще кажете за вариадни аргументи и/или < 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
Не съм убеден, че това е невъзможно. Вижте редакция: Вариадни аргументи и Пакет с параметри - person Vector; 10.12.2014
comment
@Vector Вие сте по-добри с препроектирането, отколкото с опитите да използвате различни аргументи. Всъщност, както беше казано тук, можете да използвате изтриване на типа, но след това трябва да поставите върнатата стойност в структурата. - person BЈовић; 10.12.2014
comment
@BЈовић - никога не е работил с различни аргументи и не изглеждат добре; кастингът void* просто не е добър вариант за проектиране IMO, въпреки че би бил по-лесен за управление от подхода на cludgy tuple, който е много грозен. Започвам да си мисля, че просто трябва да забравя картата и да използвам switch. - person Vector; 10.12.2014
comment
Да, използвайте превключвател. Ако можете да избегнете тази битка, направете го. - person StilesCrisis; 10.12.2014