LINUX.ORG.RU

Как размножить собитие с переменным количеством параметров.

 , ,


0

3

Пусть будут классы:

template <class F> struct Receptor;

// F это function<void(...)>

template <class F>
struct Sender {
    F use; // вызов sndr.use(a,b,c) должен размножится в list_use
    std::vector<Receptor<F>*> list_use;
};

template <class F>
struct Receptor {
    F process;
    Sender<F> * sender = nullptr;
    ~Receptor() { /* удаление из sender текущего Receptor */ }
};

Можно ли как либо сделать универсально Sender::use, что бы выполнив sndr.use(1,2,3) оно обошло все элементы list_use и выполнило вызов соотвествующих process с указанными параметрами? Т.е. размножило один вызов use на весь список.

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

function(F, ...)

Half-Lambda ()
Ответ на: комментарий от Half-Lambda

Если я правильно понял, вы хотите пройтись по списку функций, вызывая каждую с одинаковыми параметрами?

Совершенно верно, и список параметров заранее не известен. Что бы шаблон Sender можно было использовать так:

Sender<function<void(int a)>> sender1;
Sender<function<void(int a, int b)>> sender2;
А после, все кто к ним прососется, получает себе событие, когда что-либо другое вызовет:
sender1.use(10);
У меня там еще несколько методов у классов Sender/Receptor и в целом это очень удобно использовать, только нехватает вызова по списку функций. Приходится делать дополнительный класс реализаций.

В таком случае можно создать свой класс-список

Этим я это и сделал:

std::vector<Receptor<F>*> list_use;
Receptor это обертка к F, а F это function<...>. Обертка нужна, потому что она еще запоминает в себе указатель на list_use и в случае удаления удаляет себя из list_use. И наоборот, в случае удаления Sender он чистит себя в Receptor.

function(F, ...)

Но как реализовать этот «function(F, ...)»?

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

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

Возвращаясь к вопросу о

function(F, ...)

Под списком я подразумевал свой FunctionList, где каждое звено определено по типу:

typedef void(*F)(...);
typedef struct FunctionNode {
	fnode* next;
	F use;
} fnode;
А в качестве FunctionList::Use реализовать обход списка. Если понадобится регистрация, то можно добавить в FunctionNode функцию, которая в качестве аргумента получает коллбэк на вашу
typedef void(*Action)(void (__cdecl*)(...), ...)

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

Half-Lambda ()

Мне кажется, что если принимать не F, а список аргументов, то use можно присвоить лямбду в конструкторе, которая обходит детей. Что-то вроде:

template <typename... Args>
struct Sender {
    std::function<void(Args...)> use;
    std::vector<Receptor<std::function<void(Args...)>*> list_use;

    Sender();
}

template <typename... Args>
Sender::Sender()
{
    use = [this](Args &&...args) {
        for (auto &receptor : list_use) {
            receptor.process(args...);
        }
    };
}
xaizek ★★★★★ ()
Последнее исправление: xaizek (всего исправлений: 1)
Ответ на: комментарий от DELIRIUM

я бы написал без ошибок.

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

victor79 ()