Список функций C++
Я работаю над довольно сложным проектом, пользовательской процедурой шифрования, если хотите (просто для удовольствия), и я столкнулся с этой проблемой при разработке макета кода.
У меня есть ряд функций, которые я хочу иметь возможность вызывать по индексу. В частности, мне нужно иметь возможность вызывать один случайным образом для процесса шифрования, но затем обращаться к нему по определенному индексу в процессе расшифровки.
Я думал о классическом массиве функций, но моя главная проблема в том, что массив функций будет сложно поддерживать и немного уродлив. (Цель состоит в том, чтобы собрать каждую пару функций в отдельный файл, сократить время компиляции и упростить управление кодом.) Есть ли у кого-нибудь более элегантное решение C++ в качестве альтернативы массиву функций? Скорость на самом деле не проблема, меня больше беспокоит ремонтопригодность.
-Nicholas
8 ответов
Вы могли бы написать что-то вроде:
class EncryptionFunction
{
public:
virtual Foo Run(Bar input) = 0;
virtual ~MyFunction() {}
};
class SomeSpecificEncryptionFunction : public EncryptionFunction
{
// override the Run function
};
// ...
std::vector<EncryptionFunction*> functions;
// ...
functions[2]->Run(data);
Вы могли бы использовать operator()
вместо Run
в качестве имени функции, если вы предпочитаете.
Что не так с массивом функций?
Вам нужно вызывать функции по индексу. Таким образом, они должны быть каким-то образом помещены в некую структуру, "индексируемую по индексу". Массив, вероятно, самая простая структура, которая удовлетворяет этой потребности.
Пример (печатать у меня в голове, может не скомпилироваться):
struct FunctionPair {
EncodeFunction encode;
DecodeFunction decode;
};
FunctionPair g_Functions[] = {
{ MyEncode1, MyDecode1 },
{ MySuperEncode, MySuperDecode },
{ MyTurboEncode, MyTurboDecode },
};
Что является "уродливым" или "трудным для поддержания" в подходе выше?
Попробуйте класс Loki::Functor. Больше информации на CodeProject.com
Если вы смотрели в boost::signals
библиотека, вы увидите очень хороший пример, который очень элегантный:
Предположим, у вас есть 4 функции, такие как:
void print_sum(float x, float y)
{
std::cout << "The sum is " << x+y << std::endl;
}
void print_product(float x, float y)
{
std::cout << "The product is " << x*y << std::endl;
}
void print_difference(float x, float y)
{
std::cout << "The difference is " << x-y << std::endl;
}
void print_quotient(float x, float y)
{
std::cout << "The quotient is " << x/y << std::endl;
}
Тогда, если вы хотите назвать их элегантным способом, попробуйте:
boost::signal<void (float, float)> sig;
sig.connect(&print_sum);
sig.connect(&print_product);
sig.connect(&print_difference);
sig.connect(&print_quotient);
sig(5, 3);
И вывод:
The sum is 8
The product is 15
The difference is 2
The quotient is 1.66667
Вам нужно использовать массив указателей на функции. Единственный улов заключается в том, что все функции должны иметь в основном один и тот же прототип, могут меняться только имя функции и имена переданных аргументов. Тип возвращаемого значения и типы аргументов (а также количество аргументов и порядок) должны быть идентичны.
int Proto1( void );
int Proto2( void );
int Proto3( void );
int (*functinPointer[3])( void ) =
{
Proto1,
Proto2,
Proto3
};
Тогда вы можете сделать что-то вроде этого:
int iFuncIdx = 0;
int iRetCode = functinPointer[iFuncIdx++]();
Полиморфизм может помочь: вы можете следовать шаблону стратегии, рассматривая каждую стратегию для реализации одной из ваших функций (или пары из них).
Затем создайте вектор стратегий и используйте его вместо списка функций.
Но, честно говоря, я не вижу проблемы с массивом функций; Вы можете легко создать typedef для облегчения чтения. Эффективно, вы получите точно такую же файловую структуру при использовании шаблона стратегии.
// functiontype.h
typedef bool (*forwardfunction)( double*, double* );
// f1.h
#include "functiontype.h"
bool f1( double*, double* );
// f1.c
#include "functiontype.h"
#include "f1.h"
bool f1( double* p1, double* p2 ) { return false; }
// functioncontainer.c
#include "functiontype.h"
#include "f1.h"
#include "f2.h"
#include "f3.h"
forwardfunction my_functions[] = { f1, f2, f3 };
- Объявление функции и определения находятся в отдельных файлах - время компиляции в порядке.
- Группировка функций находится в отдельном файле, имеющем зависимость только от объявлений
Вы можете взглянуть на библиотеку Boost.Signals. Я считаю, что он имеет возможность вызывать свои зарегистрированные функции с помощью индекса.
Объект с определенным методом operator() может вести себя как функция, но в целом работать с ним приятнее.