Могут ли вариадические функторы в C ++ поддерживать именованные поля? - PullRequest
0 голосов
/ 31 мая 2018

Я реализую универсальный функтор с переменным именем "Signal<args>", который управляет внутренней очередью функторов с соответствующими типами сигнатур функций.При вызове Signal<args>::operator() каждый функтор в очереди выполняется с одинаковыми аргументами ввода.Идея состоит в том, что это чистый объектный тип C ++ 11, который воссоздает некоторые из тех же конструктивных особенностей, что и конструкция Qt Signal / Slot, но статически компилируется с минимальными зависимостями.У меня все работает, но я хотел бы улучшить читаемость этого универсального.

Синтаксис у меня есть:

Signal<int, double> signal;
signal.add(BLOCK, [](int EmployeeID, double FavoriteNumber) {
    std::cout << EmployeeNames[EmployeeID]
              << " has favorite number of "
              << FavoriteNumber << std::endl;
});
signal.add(BLOCK, [](int EmployeeID, double FavoriteNumber) {
    if (EmployeeID > FavoriteNumber) {
        std::cout << EmployeeNames[EmployeeID]
                  << " has ID bigger than favorite number.\n";
    }
});
signal(5, 3.1415); //execute both functors with args = (5, 3.1415)

Что бы я хотел иметь:

Signal<int EmployeeID, double FavoriteNumber> signal;
signal.add(BLOCK, [](int EmployeeID, double FavoriteNumber) {
    std::cout << EmployeeNames[EmployeeID]
              << " has favorite number of "
              << FavoriteNumber << std::endl;
});
signal.add(BLOCK, [](int EmployeeID, double FavoriteNumber) {
    if (EmployeeID > FavoriteNumber) {
        std::cout << EmployeeNames[EmployeeID]
                  << " has ID bigger than favorite number.\n";
    }
});
signal(5, 3.1415); //execute both functors with args = (5, 3.1415)

Единственное отличие состоит в том, что я хотел бы, чтобы объявление типа шаблонного сигнала указывало имя параметра для удобства чтения.В идеале я хотел бы, чтобы эти имена были обязательными и не компилировались, если не указаны.

Есть ли способ добиться этого?

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Простым решением является использование типов тегов, таких как

struct EmployeeID_T {};
struct FavoriteNumber_T {};

и привязка типа аргумента к имени, например

using EmployeeID = SignalArg<EmployeeID_T, int>;
using FavoriteNumber = SignalArg<FavoriteNumber_T, double>

, чтобы вы получили

Signal<EmployeeID, FavoriteNumber> mySignal;
signal.add(BLOCK, [](EmployeeID eid, FavoriteNumber fn) {
    std::cout << EmployeeNames[eid]
              << " has favorite number of "
              << fn << std::endl;
});
signal.add(BLOCK, [](EmployeeID eid, FavoriteNumber fn) {
    if (eid > fn) {
        std::cout << EmployeeNames[eid]
                  << " has ID bigger than favorite number.\n";
    }
});
mySignal(5, 3.1415); //execute both functors with args = (5, 3.1415)

Вам необходим шаблон SignalArg с соответствующими конструкторами и операторами преобразования, но, поскольку я его не записал, может потребоваться некоторое принуждение.

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

Вы также можете получить что-то вроде именованных параметров, сделав явным конструктор преобразования SignalArg, поэтому вместо этого вызов будет выглядеть примерно так:

mySignal(EmployeeID{5}, FavoriteNumber{3.1415});
0 голосов
/ 31 мая 2018

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

Signal<void(int EmployeeID, double FavoriteNumber)> mySignal;

Имя не обязательно, но разрешено.

Для этого нужно иметь частичную специализацию для Signal:

template <typename>
class Signal;

template <typename... Args>
class Signal<void(Args...)> {
    // Old implementation of Signal<...>
};
...