Я пытаюсь найти способ иметь контейнер функторов, чтобы я мог передать значение в функторы и изменить его, однако у меня возникают проблемы с тем, чтобы функторы не были ограничены в том, какие типы они могут быть передано и количество аргументов, которые они могут принять.
Мое фактическое использование для этого состоит в том, что у меня есть ряд функторов, которые все так или иначе меняют трехмерный вектор на основе входных данных. Имея возможность хранить эти функторы в контейнере, я могу манипулировать порядком, в котором они вызываются, и получать в результате другой результирующий вектор, перебирая контейнер, передавая каждому функтору вектор. Я использую эти векторы, чтобы определить положение, цвет, ускорение и т. Д. Частицы. Таким образом, в конечном итоге я могу создавать совершенно разные эффекты частиц, применяя этот модульный подход, и в конечном итоге я могу задать порядок функторов в файле во время выполнения. Это моя конечная цель:)
Единственный способ, которым я сделал эту работу, - это наследование и набор пустых указателей, что делает код чрезвычайно сложным для отслеживания, отладки и использования.
Вот мой код, который, надеюсь, продемонстрирует, что я пытаюсь сделать лучше, чем то, что я напечатал выше. Пожалуйста, имейте в виду, что я вне моей зоны комфорта, поэтому этот код может быть ужасным, и некоторые из вас, гуру, хотят избить меня палкой.
#include <iostream>
#include <vector>
//the base functor class
struct Functor {
//an integer so we can tell how many arguments the functor takes
unsigned num_arguments;
Functor() : num_arguments(0) {}
//I'm making the arguments default to 0 so the compiler doesn't complain about not giving enough arguments
virtual void operator()(void* R, void* A1 = 0, void* A2 = 0, void* A3 = 0) = 0;
};
template<typename R, typename A1>
struct Double : public Functor {
Double() { num_arguments = 1; }
void operator()(void* i, void* a, void*, void*) {
//having to cast everything out of void pointers so it can be used
A1& first = *static_cast<A1*>(a);
*static_cast<R*>(i) = first * 2;
}
};
template<typename R, typename A1, typename A2>
struct Sub : public Functor {
Sub() { num_arguments = 2; }
void operator()(void* i, void* a, void* b, void*) {
//having to cast everything out of void pointers so it can be used
A1& first = *static_cast<A1*>(a);
A2& second = *static_cast<A2*>(b);
*static_cast<R*>(i) = first - second;
}
};
int main() {
//our container for the functors
std::vector<Functor*> functors;
functors.push_back(new Double<int, int>);
functors.push_back(new Sub<int, int, int>);
for(int i = 0; i < functors.size(); ++i) {
int result;
int first = 1, second = 2;
Functor& f = *functors[i];
if(f.num_arguments == 1) {
f(&result, &first);
} else if(f.num_arguments == 2){
f(&result, &first, &second);
}
std::cout << result << std::endl;
}
Functor* float_sub = new Sub<float, float, float>;
float result;
float first = 0.5f, second = 2.0f;
(*float_sub)(&result, &first, &second);
std::cout << result << std::endl;
functors.push_back(float_sub);
//The functors vector now contains 3 different types of functors:
//One that doubles an integer
//One that subtracts two integers
//One that subtracts two floats
std::cin.get();
return 0;
}
Примечание:
Я ожидаю, что некоторые люди скажут мне использовать то или иное из библиотек наддува. Несмотря на то, что я ценю знание этой возможности, я все же хотел бы узнать лучший способ реализовать это без каких-либо внешних библиотек, поскольку для меня это что-то вроде учебного упражнения.
Редактировать
Хорошо, так что, узнав о stdarg
и boost::any
, я думаю, что я вижу способ сделать эту работу красиво и пробую это:)
Раствор 2
Хорошо, я переработал код, используя boost::any
и cstdarg
для того, что я считаю лучшим решением. Это не использует пустые указатели и не ограничивает количество аргументов, которые могут иметь функторы. Более новый код также позволяет передавать по значению, используя пустые указатели, все должно быть по ссылке, что может вызвать проблемы при попытке сделать: Sub(&result, 1, 1)
#include <iostream>
#include <vector>
#include <cstdarg>
#include <boost\any.hpp>
struct Functor {
unsigned num_arguments;
Functor() : num_arguments(0) {}
virtual void operator()(boost::any, ...) = 0;
};
template<typename R, typename A1>
struct Double : public Functor {
Double() { num_arguments = 1; }
void operator()(boost::any r, ...) {
R* out = boost::any_cast<R*>(r);
va_list args;
va_start(args, r);
A1 first = va_arg(args, A1);
va_end(args);
*out = first * 2;
}
};
template<typename R, typename A1, typename A2>
struct Sub : public Functor {
Sub() { num_arguments = 2; }
void operator()(boost::any r, ...) {
R* out = boost::any_cast<R*>(r);
va_list args;
va_start(args, r);
A1 first = va_arg(args, A1);
A2 second = va_arg(args, A2);
va_end(args);
*out = first - second;
}
};
int main() {
std::vector<Functor*> functors;
functors.push_back(new Double<int, int>);
functors.push_back(new Sub<int, int, int>);
functors.push_back(new Sub<int, float, float>);
int result = 0;
for(int i = 0; i < functors.size(); ++i) {
(*functors[i])(&result, 2, 2);
std::cout << result << std::endl;
}
std::cin.get();
return 0;
}
и теперь у меня наконец есть привилегия для голосования: D