中间如何组织两个不同的功能

时间:2018-08-14 06:24:02

标签: c++

我想问一下如何很好地组织以下两个功能。

很抱歉没有上传完整的代码。

void prepend_float(t_ptr *x, float f)
{
    int ac = x->num;
    t_atom *av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    av[0].type = A_FLOAT; //not common
    av[0].f = f;          //not common
    for (int i = 1; i < ac; ++i)
    {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}

void prepend_string(t_ptr *x, std::string s)
{
    int ac = x->num;
    t_atom *av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    av[0].type = A_STRING; //not common
    av[0].s = s;           //not common
    for (int i = 1; i < ac; ++i)
    {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}

如您所见,这两个函数具有相同的代码,但我标记了not common的两行除外。

如何创建通用函数来减少重复的代码?

我现在能想到的是创建一个具有以下四个参数的通用函数。

void common_function(t_ptr *x, float f, std::string, t_type type)
{
    int ac = x->num;
    t_atom *av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    if (type == A_FLOAT)
    {
        av[0].type = type;
        av[0].f = f;   
    }
    else if (type == A_STRING)
    {
        av[0].type = type;
        av[0].s = s; 
    }       
    for (int i = 1; i < ac; ++i)
    {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}

是否有更好的方法来组织这两个功能?

4 个答案:

答案 0 :(得分:4)

我的建议:保留公共部分并将差异转换为参数。

在您的情况下,区别在于两个语句。可以使用函数将其抽象出来。 std::function使我们能够处理任何可调用对象,而lambda表达式使传递自定义函数(由需要在通用代码中间执行的两个特定于类型的语句组成)的传递变得容易:

void prepend_something(t_ptr *x, std::function<void (t_atom &)> init)
{
    int ac = x->num;
    t_atom *av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    init(av[0]);
    for (int i = 1; i < ac; ++i)
    {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}

void prepend_float(t_ptr *x, float f)
{
    prepend_something(x, [f](t_atom &a) { a.type = A_FLOAT; a.f = f; });
}

void prepend_string(t_ptr *x, std::string s)
{
    prepend_something(x, [s](t_atom &a) { a.type = A_STRING; a.s = s; });
}

答案 1 :(得分:4)

有几种选择。经典方法是将通用代码封装到要从有问题的两个函数中调用的函数中:

void f()
{
    common_pre(); // parameters/return value as needed
    specific_f();
    common_post();
}

void g()
{
    common_pre();
    specific_g();
    common_post();
}

您可以将通用代码打包到一个函数中,并将lambda传递给它:

template<typename Function>
void common(Function& f) // alternatively, instead of template, std::function
{
    common_pre();
    f();
    common_post();
}

void f()
{
    common([/* capture as needed*/] (/*possibly parameters...*/) { /* f specific code */ });
}

void g()
{
    common([] () { /* g specific code */ });
}

lambda方法是我在给定情况下会喜欢的方法,可能看起来像这样:

template <typename Function>
void doPrepend(t_ptr* x, Function& f)
{
    int ac = x->num;
    t_atom* av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    f(av);
    for (int i = 1; i < ac; ++i)
    {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}

void prepend_float(t_ptr* x, float f)
{
    auto assign = [f](t_atom* av)
    {
        av->type = A_FLOAT;
        av->f = f;
    };
    doPrepend(x, assign);
}

void prepend_string(t_ptr* x, std::string const& s)
{
    auto assign = [&s](t_atom* av)
    {
        av->type = A_STRING;
        av->s = s;
    };
    doPrepend(x, assign);
}

编辑:schorsch312answer的启发,而不是依赖boost和string比较,而是通过简单的函数重载来解决:

void assign(t_atom* av, float f)
{
    av->type = A_FLOAT;
    av->f = f;
}

void assign(t_atom* av, std::string const& s)
{
    av->type = A_STRING;
    av->s = s;
}

template <typename T>
void prepend(t_ptr* x, T const& t)
{
    int ac = x->num;
    t_atom* av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    assign(av, t);
    for (int i = 1; i < ac; ++i)
    {
#if 0
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
#else
        // in this approach; we can even re-use the assign function:
        assign(av + i, x->fv[i - 1]);
#endif
    }
    do_something(x, ac, av);
    free(av);
}

与lambda方法相比,它的复杂程度稍低,但差异并不明显。

它只会产生一个单一的函数名称(在前两种方法中,您可以通过重载两个函数而不是使用单独的名称来实现),但是可能会进行进一步的更改(规范,文档,已经使用该接口的代码) )。

答案 2 :(得分:1)

我将使用模板和boost type_index

template <typename T>
void prepend_string(t_ptr *x, T s) {
    int ac = x->num;
    t_atom *av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    const std::string valueType = boost::typeindex::type_id<T>().pretty_name();
    if (valueType == "float") {
       av[0].type = A_FLOAT;
       av[0].f = f;         
    } else if (valueType == "string") {
        av[0].type = A_STRING; 
        av[0].s = s;           
    } else {
         // error handling
    }

    for (int i = 1; i < ac; ++i) {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}

这适用于Linux和Windows。

答案 3 :(得分:0)

您可以使用面向对象。

创建一个抽象的BaseClass和派生类,其类型为string和float。 在av结构中,您具有对BaseClass的引用,并且可以访问抽象BaseClass的操作或获取方法。

class MyBaseType
{
     virtual Type GetType() = 0;
     /* manipulation methods .. */
     std::string GetValueAsString() = 0;
}

class MyStringType : public MyBaseType
{
     // implement abstract methods of base class
}
class MyFloatType : public MyBaseType
{
     // implement abstract methods of base class
}

void prepend(t_ptr *x, MyBaseType *MyType)
{
    int ac = x->num;
    t_atom *av = static_cast<t_atom *>(malloc(sizeof(t_atom) * ac));
    av[0] = MyType; 
    for (int i = 1; i < ac; ++i)
    {
        av[i].type = A_FLOAT;
        av[i].f = x->fv[i - 1];
    }
    do_something(x, ac, av);
    free(av);
}