LINUX.ORG.RU

Необходимо соорудить std::map с указателями на функции...

 , ,


0

2

Причём функции могут иметь различное количество параметров.
Всё что крутится в голове это превратить функции в такое убожество: type function(double var, ...)
Я пока не пробовал делать указатель на функцию с переменным количеством параметров, но даже если это возможно, то мне не нравится идея превращения множества красивых функций в это уродство.
А если уж и превращать, то тогда и вовсе отказаться от std::map и указателей на функции и сделать одну огромную фукцию, принимающую в качестве параметров «название ф-ции», количество параметров ну и это сраное троеточие...
Оба метода некрасивы на мой взгляд. Кто-то сталкивался с чем-то подобным? Как решали вопрос?

★★☆

С-way - кастовать указатели на функцию в в void* и обратно, в std::map хранить void-указатели

C++ way (дальше кровь-кишки-хардкор, слабонервным не читать) - сделать абстрактный класс с переопределённым оператором (), в std::map хранить указатели на этот базовый класс, вместо функций делать унаследованные объекты с переопределённым оператором (), вызывающим нужную функцию

Harald ★★★★★
()
Ответ на: комментарий от Harald

void-указатели

Не хочу я брать на себя такую ношу. Плохо это. И закончится кучей невменяемого говнокода.

переопределённым оператором ()

Я лучше int на ноль поделю — проще и эффективней...

Спасибо, конечно, но что-то твои варианты совсем грустные...
Подожду ещё идей и пойду писать мегафункцию...

Stahl ★★☆
() автор топика

И как ты их использовать собрался?

O02eg ★★★★★
()

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

const86 ★★★★★
()
Ответ на: комментарий от const86

Хм... И правда. Что-то я разогнался. Я либо туплю уже под вечер, либо ты меня сбил с очень умной мысли.

Stahl ★★☆
() автор топика
Ответ на: комментарий от Stahl

Пойду-ка я выпью пива да подумаю что я собственно хотел...

Stahl ★★☆
() автор топика
Ответ на: комментарий от Stahl

на С пишут смелые люди, на С++ - тем более, не нравится - 1С ждёт тебя :)

переопределённым оператором ()

это кстати стандартная практика, называется «функциональный объект»

Harald ★★★★★
()
Ответ на: комментарий от Harald

С / С++ / 1С Какой-то у тебя небольшой выбор

Aswed ★★★★★
()
Ответ на: комментарий от intelfx

Хотя нет, я идиот. std::function нужен для другого.

Так что кастовать в void*, если ты в момент вытаскивания функции из map-а точно знаешь, что она действительно принимает те параметры, которые ты хочешь ей передать. (А если не знаешь, то постановка задачи некорректна.)

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 3)

Что-то типа такого?

void function_with_params_1(int a);
void function_with_params_2(int a1, int a2);

map<int, function<void()>> m;

int a;
m[0] = [&a]() { function_with_params_1(a); };

int a1, a2;
m[1] = [&a1, &a2]() { function_with_params_2(a1, a2); };

anatoly
()
Ответ на: комментарий от Stahl

Так эта... Переходи на тёмную сторону Силы. Всё равно boost, фактически, полигон для обкатки новых возможностей плюсов. Глядишь, к следующему стандарту и добавят чего вкусного из boost-а в Pure C++.

one_more_hokum ★★★
()
Ответ на: комментарий от batbko

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

batbko
()
Ответ на: комментарий от batbko

Вот кстати да. Если на момент помещения в контейнер уже известно, какие параметры должны быть у функций, то можно связать их placeholder-ами, и пхать в контейнер однотипные std::function<retval (void)>.

one_more_hokum ★★★
()
Ответ на: комментарий от one_more_hokum

Угу, а ещё можно без просто ключ расширить до контекста, который умеет сам правильно вызывать функцию. Вообще сильно от задачи зависит:)

batbko
()
Ответ на: комментарий от batbko

А разве сильно толстый ключ для std::map — это православно? Вспоминается мне, что ейные потроха очень грустно работают на тяжёлых ключах. С реализацией связано, вроде.

one_more_hokum ★★★
()
Ответ на: комментарий от one_more_hokum

А зачем тяжелый? Данные можно по указателю хранить, главное, что бы operator < быстро работал.

Имхо от автора нужны ответы минимум на 2вопроса.

Каким образом параметры становятся доступны в точке вызова? Каковы типы параметров?

batbko
()
Ответ на: комментарий от batbko

от автора нужны ответы минимум на 2 вопроса

Причём самому автору они гораздо нужнее.

one_more_hokum ★★★
()

Тебе надо произвольное кол-во параметров? Не вопрос.

Делай как-то так.

class IParam {
}
class Param : public IParam {
public:
 int a;
 int b;
 std::string str;
}

map<std::string, std::function<void(IParam&)>> functions;

invy ★★★★★
()

Вот тебе говнокод :)

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

class IParam {
};
class Param1 : public IParam {
public:
 Param1(int a, int b, const std::string& s) : a(a), b(b), str(s) {}
 int a;
 int b;
 std::string str;
};
class Param2 : public IParam {
public:
 Param2(const std::string& s) : str(s) {}
 std::string str;
 int len;
};

void f1(const IParam& ip) {
  Param1& p = (Param1&)ip;
  std::cout << p.a << " " << p.b << " " << p.str << "\n";
}

void f2(const IParam& ip) {
  Param2& p = (Param2&)ip;
  std::cout << p.str << "\n";
  p.len = p.str.size();
}

int main() {
 using namespace std::placeholders;

 std::unordered_map<int, std::function<void(const IParam&)>> functions;
 functions[1] = std::bind(f1, _1);;
 functions[55] = std::bind(f2, _1);

 functions[1](Param1(1, 2, "govnokod"));

 Param2 p2("govnokod");
 functions[55](p2);

 std::cout << p2.len << "\n";

 return 0;
}

invy ★★★★★
()
Ответ на: комментарий от Stahl

Ну знаю конечно. По имени (ключу из мапа) я узнаю какие и сколько.

У тебя маршалинг, или ты пытаешься использовать строки вместо нормальных идентификаторов?

O02eg ★★★★★
()

Грязный и опасный хак:

#include <iostream>
#include <unordered_map>

using namespace std;


struct ugly_hack
{
    template<class... Args>
    void operator()( Args... p ) const { ((void (*)(Args...))(ptr))( p... ); }	
	
    void* ptr;
};


void foo1( float a, float b )
{
    cout << a << ":" << b << endl;
}

void foo2( const string& s )
{
    cout << s << endl;
}


int main()
{
    unordered_map<string, ugly_hack> m;
    m[ "foo1" ] = { (void*) foo1 };
    m[ "foo2" ] = { (void*) foo2 };
	
    m[ "foo1" ]( 1.f, 2.f );
    m[ "foo2" ]( string( "test" ) );
}

Но тебе нужен не он, а разобраться в том, что тебе на самом деле нужно сделать, и сделать это без всяких мап.

anonymous
()

Помнится лет 10 тому назад, я ради дзена делал простенький интерпретатор. И вот там мне пришлось таки хранить указатели на функции с переменным числом параметров. В хранении проблем небыло. Все кастовалось к void*.
А вот при вызове надо было динамически определить кол-во аргументов их тип, и засунуть все это в стек. Емнип эту часть я вообще зафигачил на асме ).

Да, ТС, может по твоей задаче лучше будет прикрутить уже готовый интерпретатор? Благо их сейчас на вкус и цвет дофигища.

AF ★★★
()
Ответ на: комментарий от AF

Я прокрутил в голове предложенные варианты и всё-таки решил отказаться от массива указателей и сделать одну большую ф-цию.
Код должен быть простым и понятным, а не void operator()( Args... p ) const { ((void (*)(Args...))(ptr))( p... ); }

Stahl ★★☆
() автор топика
Ответ на: комментарий от Stahl

У одной большой функции будет конечное число возможных вариантов по количеству параметров и их типу. Если тебя это утроит, то такой вариант имеет право на жизнь. Хотя, конечно, лучше ее разбить на составные части, ибо запаришся потом ее поддерживать. Да и вообще, огромные switch-case имеют вкус оффтопика )

Если ограничится вызовом cdecl ф-й, то можно сделать следующий финт:

1)Код, которые знает о типе аргументов и кол-ве аргументов, складывает их в стек и вызывает диспетчер. Диспетчеру передается ключ, по которому ищется нужная функция
2)Диспетчер находит адрес нужной ф-ии в мапе и вызывает ее, как функцию без аргументов. Тут хитрость. Вызвать надоо не ч-з call, а ч-з косвенный jmp. Таким образом в вызванной функции окажется правильный стек со всеми аргументами.

По идее все этот можно попробовать реализовать даже без асма. __attribute__ ((naked)) в нужных местах должно хватить.

AF ★★★
()
Ответ на: комментарий от Stahl

Я прокрутил в голове предложенные варианты и всё-таки решил отказаться от массива указателей

Так ещё в голове покрути, а то из сказанного тобой не понятно, то ли ты хочешь свой интерпретатор, то ли у тебя есть набор функций с различными реализациями, то ли ещё что-то. Если первое, то ты не по той дороге пошёл. Если второе, то используй структуру для хранения ссылок на функции.

AlexVR ★★★★★
()
Ответ на: комментарий от AF

по количеству параметров и их типу

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

switch-case имеют вкус оффтопика

Это что-то из писаний свидетелей goto?:)

__attribute__ ((naked))

Я таким не пользовался, но как тут с переносимостью? А на powerPC или MIPS заработает?

Stahl ★★☆
() автор топика
Ответ на: комментарий от Stahl

Я таким не пользовался, но как тут с переносимостью? А на powerPC или MIPS заработает?

Должно работать. Но powerPC или MIPS у меня нет, так что пусть эксперты подвердят или опровергнут.

Этот вариант привязывается к cdecl. Т е аргументы функции передаются ч-з стек и в порядке обратном обьявленном в функции + стек чистит вызывающая функция.

Да, я пишу специфический интерпретатор.

Я так понимаю, стековая машина?

AF ★★★
()
Ответ на: комментарий от AF

Гм. Сложно сказать. Вдохновлялся я «алгоритмом ж\д депо», но по факту х.з. что получилось, но работает:)

Stahl ★★☆
() автор топика
Ответ на: комментарий от Stahl

решил отказаться от массива указателей и сделать одну большую ф-цию.
Код должен быть простым и понятным, а не void operator()( Args... p ) const { ((void (*)(Args...))(ptr))( p... ); }

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

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

#include <iostream>
#include <typeinfo>
#include <unordered_map>

using namespace std;


struct ugly_hack
{
    template<class... Args>
    void operator()( Args... p ) const 
    {
        auto f = (void (*)(Args...))( ptr );
        if( typeid( f ).hash_code() == id )
            f( p... );
    }	
	
    size_t id;
    void*  ptr;
};

template<class T>
ugly_hack ugly_hack_factory( T* ptr )
{
    return { typeid( ptr ).hash_code(), (void*) ptr };
}


void foo1( float a, float b )
{
    cout << a << ":" << b << endl;
}

void foo2( const char* s )
{
    cout << s << endl;
}


int main()
{
    unordered_map<string, ugly_hack> m 
    {
        { "foo1", ugly_hack_factory( foo1 ) },
        { "foo2", ugly_hack_factory( foo2 ) }
    };
	
    m[ "foo1" ]( 1.f, 2.f );
    m[ "foo2" ]( "test" );
    m[ "foo2" ]( 1 );
}
anonymous
()
Ответ на: комментарий от Stahl

Не хочу я брать на себя такую ношу. Плохо это. И закончится кучей невменяемого говнокода.

это самый вменяемый вариант из возможных

Подожду ещё идей и пойду писать мегафункцию

держи три:

1) юзай python или любой другой динамический язык

2) сделай эти функции скриптами на том же python/lua/js лучше не надо

3) изложи саму задачу, а не свой вариант ее извращения. возможно, какой-нибудь умеренно сложный паттерн избавит от необходимости мучиться с функциями разных типов

MyTrooName ★★★★★
()
Ответ на: комментарий от anonymous

не делает его сложным

Делает. Как минимум для меня. А код это дело моё и компилятора.

Твой код вроде осмысленный, но я так не пишу. Я действительно его не понимаю. По крайней мере с листинга.
Но поэкспериментировать надо. Спасибо. Тем более, что результат (способ вызова ф-ций) как раз то, что мне нужно. Просто идеально.

Stahl ★★☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.