C++ 使用枚举指定的函数填充向量

C++ 使用枚举指定的函数填充向量,c++,enums,lambda,factory,C++,Enums,Lambda,Factory,我想要的功能如下: std::vector<float> GetFuncVec(int N, FuncType type) { std::vector<float> fn(N); float tmp = (N - 1) / 2.0; switch (type) { case SIN: for (int i=0; i<N; ++i) fn[i] = sin(M_PI * i / tmp);

我想要的功能如下:

std::vector<float> GetFuncVec(int N, FuncType type)
{
    std::vector<float> fn(N);
    float tmp = (N - 1) / 2.0;

    switch (type) {
    case SIN:
        for (int i=0; i<N; ++i)
            fn[i] = sin(M_PI * i / tmp);
        break;
    case SINC:
        for (int i=0; i<N; ++i)
            fn[i] = sin(M_PI * i / tmp) / (M_PI * i / tmp);
        break;
    ...
    }

    return fn;
}
std::vector GetFuncVec(int N,FuncType类型)
{
std::载体fn(N);
浮动tmp=(N-1)/2.0;
开关(类型){
案例SIN:

对于(int i=0;i也许您想要这样的东西…?(看到了吗


也许你想要这样的东西…?(看到了吗


一个可能不快(每个函数调用都有间接寻址)但更灵活的选项是创建一个
std::map
。您不能使用
std::generate()
,因为您需要参数
i
来计算结果,但编写自己的结果并不难:

template <typename Iterator, typename Generator, typename Index, typename... Args>
void generate_i(Iterator first, Iterator last, Generator gen, Index i, Args... args)
{
    while (first != last) {
        *first = gen(i, args...);
        ++i;
        ++first;
    }
}
模板
void generate_ui(迭代器优先,迭代器last,Generator,Index i,Args…Args)
{
while(第一个!=最后一个){
*第一个=发电机(i,args…);
++一,;
++第一,;
}
}
现在我们有了这个,我们需要填充一个函子映射:

using FuncTypeFunction = std::function<float(int,float)>;
using FuncTypeFunctionMap = std::map<FuncType, FuncTypeFunction>;

FuncTypeFunctionMap create_functype_map()
{
    FuncTypeFunctionMap functions;

    functions[SIN]  = [] (int i, float tmp) {
        return sin(M_PI * i / tmp);
    };

    functions[SINC] = [] (int i, float tmp) {
        return sin(M_PI * i / tmp) / (M_PI * i / tmp);
    };

    // ...

    return functions;
}

FuncTypeFunctionMap const FuncTypeFunctions = create_functype_map();
使用FuncTypeFunction=std::function;
使用FuncTypeFunctionMap=std::map;
FuncTypeFunctionMap创建函数类型映射()
{
FuncTypeFunctionMap函数;
函数[SIN]=[](int i,float tmp){
返回sin(M_PI*i/tmp);
};
函数[SINC]=[](int i,浮点tmp){
返回sin(mu-PI*i/tmp)/(mu-PI*i/tmp);
};
// ...
返回函数;
}
FuncTypeFunctionMap常量FuncTypeFunctions=创建函数类型映射();
(如果愿意,可以使用boost.assign提高该位的可读性。)

最后,我们可以使用这张地图:

std::vector<float> GetFuncVec(int N, FuncType type)
{
    std::vector<float> fn(N);
    float tmp = (N - 1) / 2.0;

    auto func = FuncTypeFunctions.find(type);
    if (func != FuncTypeFunctions.end()) {
        generate_i(fn.begin(), fn.end(), func->second, 0, tmp);
    }

    return fn;
}
std::vector GetFuncVec(int N,FuncType类型)
{
std::载体fn(N);
浮动tmp=(N-1)/2.0;
auto func=FuncTypeFunctions.find(type);
if(func!=FuncTypeFunctions.end()){
生成_i(fn.begin(),fn.end(),func->second,0,tmp);
}
返回fn;
}
添加新函数只需要在
create\u functype\u map()
中填充映射。请注意,
generate\u i()
循环中的每次迭代都将调用
操作符()
std::function
上的
,这将需要一定程度的间接寻址来解析调用,类似于虚拟方法调用的开销。这将在性能方面花费一些成本,但对您来说可能不是问题


()

一个可能不快(每个函数调用都有间接寻址)但更灵活的选项是创建一个
std::map
。您不能使用
std::generate()
,因为您需要参数
i
来计算结果,但编写自己的结果并不难:

template <typename Iterator, typename Generator, typename Index, typename... Args>
void generate_i(Iterator first, Iterator last, Generator gen, Index i, Args... args)
{
    while (first != last) {
        *first = gen(i, args...);
        ++i;
        ++first;
    }
}
模板
void generate_i(迭代器优先、迭代器最后、生成器生成、索引i、Args…Args)
{
while(第一个!=最后一个){
*第一个=发电机(i,args…);
++一,;
++第一,;
}
}
现在我们有了这个,我们需要填充一个函子映射:

using FuncTypeFunction = std::function<float(int,float)>;
using FuncTypeFunctionMap = std::map<FuncType, FuncTypeFunction>;

FuncTypeFunctionMap create_functype_map()
{
    FuncTypeFunctionMap functions;

    functions[SIN]  = [] (int i, float tmp) {
        return sin(M_PI * i / tmp);
    };

    functions[SINC] = [] (int i, float tmp) {
        return sin(M_PI * i / tmp) / (M_PI * i / tmp);
    };

    // ...

    return functions;
}

FuncTypeFunctionMap const FuncTypeFunctions = create_functype_map();
使用FuncTypeFunction=std::function;
使用FuncTypeFunctionMap=std::map;
FuncTypeFunctionMap创建函数类型映射()
{
FuncTypeFunctionMap函数;
函数[SIN]=[](int i,float tmp){
返回sin(M_PI*i/tmp);
};
函数[SINC]=[](int i,浮点tmp){
返回sin(mu-PI*i/tmp)/(mu-PI*i/tmp);
};
// ...
返回函数;
}
FuncTypeFunctionMap常量FuncTypeFunctions=创建函数类型映射();
(如果愿意,可以使用boost.assign提高该位的可读性。)

最后,我们可以使用这张地图:

std::vector<float> GetFuncVec(int N, FuncType type)
{
    std::vector<float> fn(N);
    float tmp = (N - 1) / 2.0;

    auto func = FuncTypeFunctions.find(type);
    if (func != FuncTypeFunctions.end()) {
        generate_i(fn.begin(), fn.end(), func->second, 0, tmp);
    }

    return fn;
}
std::vector GetFuncVec(int N,FuncType类型)
{
std::载体fn(N);
浮动tmp=(N-1)/2.0;
auto func=FuncTypeFunctions.find(type);
if(func!=FuncTypeFunctions.end()){
生成_i(fn.begin(),fn.end(),func->second,0,tmp);
}
返回fn;
}
添加新函数只需要在
create\u functype\u map()
中填充映射。请注意,
generate\u i()
循环中的每次迭代都将调用
操作符()
std::function
上的
,这将需要一定程度的间接寻址来解析调用,类似于虚拟方法调用的开销。这将在性能方面花费一些成本,但对您来说可能不是问题


()

您可以编写一个通用类,用于标准算法
std::iota

比如说

#include <iostream>
#include <functional>
#include <vector>
#include <numeric>

class Value
{
public:
    Value() : i( 0 ), fn( []( size_t i ) { return ( float )i; } ) {}
    Value & operator ++() { ++i; return *this; }
    operator float () const { return fn( i ); }
    Value & operator =( std::function<float( size_t )> fn )
    {
        this->fn = fn;
        return *this;
    }
private:
    size_t i;
    std::function<float( size_t )> fn;
};


enum E { First, Second };

std::vector<float> f( size_t N, E e )
{

    Value value;

    float tmp = N / 2.0f;

    switch( e )
    {
    case First:
        value = [tmp] ( size_t i ) { return i * tmp; };
        break;

    case Second:
        value = [tmp] ( size_t i ) { return i * tmp + tmp; };
        break;
    }

    std::vector<float> v( N );

    std::iota( v.begin(), v.end(), value );

    return v;
}

int main() 
{
    for ( float x : f( 10, First ) ) std::cout << x << ' ';
    std::cout << std::endl;

    for ( float x : f( 10, Second ) ) std::cout << x << ' ';
    std::cout << std::endl;

    return 0;
}

当然,您可以使用自己的lambda表达式,其中包括一些数学函数,如
sin

您可以编写一个通用类,用于标准算法
std::iota

比如说

#include <iostream>
#include <functional>
#include <vector>
#include <numeric>

class Value
{
public:
    Value() : i( 0 ), fn( []( size_t i ) { return ( float )i; } ) {}
    Value & operator ++() { ++i; return *this; }
    operator float () const { return fn( i ); }
    Value & operator =( std::function<float( size_t )> fn )
    {
        this->fn = fn;
        return *this;
    }
private:
    size_t i;
    std::function<float( size_t )> fn;
};


enum E { First, Second };

std::vector<float> f( size_t N, E e )
{

    Value value;

    float tmp = N / 2.0f;

    switch( e )
    {
    case First:
        value = [tmp] ( size_t i ) { return i * tmp; };
        break;

    case Second:
        value = [tmp] ( size_t i ) { return i * tmp + tmp; };
        break;
    }

    std::vector<float> v( N );

    std::iota( v.begin(), v.end(), value );

    return v;
}

int main() 
{
    for ( float x : f( 10, First ) ) std::cout << x << ' ';
    std::cout << std::endl;

    for ( float x : f( 10, Second ) ) std::cout << x << ' ';
    std::cout << std::endl;

    return 0;
}

当然,您可以使用自己的lambda表达式,其中包括一些数学函数,如
sin

“lambda函数在switch语句的范围外不可访问”--为什么不可以?“lambda函数在switch语句的范围外不可访问”--为什么不呢?它的优点是包含在单个函数中
f()
。但是,我更喜欢在定义每个函数时使用枚举,以防顺序发生变化。这应该是包括
Func::NFuncs
enum的问题,对吗?是-
NFuncs
是一种合理的编译时方法,可以调整
fns[]
,然后你必须确保你没有忘记插入一个元素。顺便说一句-如果你的调用代码总是硬编码其中一个函数,你可以使用
模板f()
,而根本不需要数组,然后调用ala
auto x=f(10)
…很可能更快更可靠,但我想你知道这一点,而且这不是你的使用scanerio…对。我想保留在运行时配置函数的功能。谢谢。@DavidHall只是在想…你可以有
std::pair={Sin,[&]{Sinc,[&]},{Sinc,[&]}};
-然后对
fns
进行迭代,可以检查索引是否在增加…证明顺序没有改变(虽然可能是运行时)-co