代码之家  ›  专栏  ›  技术社区  ›  Zack Lee

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

c++
  •  2
  • Zack Lee  · 技术社区  · 6 年前

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

    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 .

    如何创建公共函数来减少重复的代码?

    我现在能想到的是创建一个公共函数,它有4个参数,如下所示。

    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 回复  |  直到 6 年前
        1
  •  4
  •   Aconcagua    6 年前

    有几种选择。经典的方法是将公共代码封装为从两个函数调用的函数:

    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);
    }
    

    编辑: schorsch312 answer 但是,不依赖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);
    }
    

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

        2
  •  4
  •   melpomene    6 年前

    我的建议是:保留共同点,把差异化为参数。

    在你的情况下,区别在于两种说法。可以通过函数抽象出来的。 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; });
    }
    
        3
  •  1
  •   schorsch312    6 年前

    我会用一个模板 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);
    }
    

        4
  •  0
  •   omar    6 年前

    可以使用对象方向。

    使用string和float类型创建抽象基类和派生类。 在av结构中,有对基类的引用,可以访问抽象基类的操作或获取方法。

    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);
    }