【问题标题】:Convenienve of macros in case of a large template argument list在模板参数列表较大的情况下使用宏的便利性
【发布时间】:2018-04-30 12:17:30
【问题描述】:

在我的设计中,我有一个针对大量参数进行模板化的类(让它成为Shell)。这些参数中的大多数是我在项目中定义的其他类,因为我为我的项目选择了基于策略的设计方法。

类声明如下所示:

template <class int_t, class float_t, class Pol1, class Pol2,..., class PolN>
class Shell : Pol1, Pol2,...,PolN

前两个参数是应使用的整数和浮点类型。其余参数是项目中定义的特定策略。

这种设计对我来说很方便,因为它可以避免很多运行时检查(我们的目标是运行时性能)。然而,每当他/她想要创建Shell 类的实例时,输入一个包含 10 多个参数的列表是非常麻烦的(从用户的角度来看)。

出于这个原因,我选择将这个打字负担连同宏一起移到一个单独的文件中。首先,我将所有策略默认为一个宏:

template <class int_t, class float_t, class Pol1 = DEF_1, class Pol2 = DEF_2,..., class PolN = DEF_N>
class Shell : Pol1, Pol2,...,PolN

模板参数可以在单独的文件中作为宏提供,而不是在求解器的声明中。例如:

#include <shell.cpp>

#define DEF_1 example1
#define DEF_2 example2
...
#define DEF_N exampleN

int main(){
    Shell<int,double> MyShell();
    return 0;
    }

这样,实例化类只需要传递给模板参数(其他+10参数通过宏传递)。定义甚至可以移动到单独的文件中,如下所示:

#include <shell.cpp>
#include "define.hpp"

这只是一种解决方法,因此不必在每次创建类的实例时都提供 10 多个参数的参数列表。宏是迄今为止我发现的最好的解决方案。但是,我知道宏在大多数 C++ 应用程序中并不是“推荐”的解决方案。

出于这个原因,我想知道这是否是一个典型问题,以及如何在没有宏的情况下克服它。我还想知道宏是否是一个“好的”解决方案,或者我是否应该不惜一切代价避免这种设计。我将不胜感激有关此主题的任何帮助/评论,因为我对 C++ 很陌生 :)

【问题讨论】:

  • 你的例子没有建立。如果包含顺序与您指定的一样,则不会替换这些宏。或者如果#define 通常排在第二位。
  • 您可以让它们在单个 typename PolicySet 中输入别名,而不是制作 Pol1..Poln 模板参数。
  • [OT]:避免包含 cpp 文件:#include &lt;shell.cpp&gt;、.hpp、.inl、.hxx 会是更好的扩展名。
  • Shell&lt;int,double&gt; MyShell();: 恼人的解析:函数声明,你可能想要Shell&lt;int,double&gt; MyShell{};Shell&lt;int,double&gt; MyShell; 代替。
  • 嗨@HolyBlackCat,这里的别名是什么意思?你的意思是像@Jarod42 提出的解决方案吗?或者更像@MaxLanghof 的解决方案?

标签: c++ templates macros policy-based-design


【解决方案1】:

这是一个完全无宏的解决方案,允许默认设置和从默认设置单独更改策略(无需重新指定默认设置),并且可以轻松扩展:

struct DefaultPol1 {};
struct DefaultPol2 {};
struct DefaultPol3 {};

struct OtherPol1 {};
struct OtherPol2 {};
struct OtherPol3 {};

template<class Pol1 = DefaultPol1, class Pol2 = DefaultPol2, class Pol3 = DefaultPol3>
struct Config
{
    using P1 = Pol1;
    using P2 = Pol2;
    using P3 = Pol3;


    template <class NewPol1>
    using ChangePol1To = Config<NewPol1, Pol2, Pol3>;

    template <class NewPol2>
    using ChangePol2To = Config<Pol1, NewPol2, Pol3>;

    template <class NewPol3>
    using ChangePol3To = Config<Pol1, Pol2, NewPol3>;
};

using DefaultConfig = Config<>;


template <class int_t, class float_t, class C = DefaultConfig>
class Shell : C::P1, C::P2, C::P3
{};


void foo()
{
    using config = DefaultConfig::ChangePol3To<OtherPol3>::ChangePol1To<OtherPol1>;
    Shell<unsigned, double, config> myShell;
}

https://godbolt.org/g/uPrRhc(改进版,感谢@Jarod42!)
(如果您将空结构定义更改为仅(前向)声明,编译器错误表明选择了正确的策略)。

命名可能会随着更多上下文而改进。

【讨论】:

  • 我真的很喜欢使用struct Config 来指定选项。事实上,我一直在寻找类似的东西——我就是无法让它发挥作用。从用户的角度来看,ChangePolXTo 有点混乱(不过这里只是一个菜鸟说话),但我绝对喜欢这种方法 :)
  • 我会将ChangePol1To 移动到Config 中,其语法与DefaultConfig::WithPol1&lt;OtherPol1&gt;::WithPol3&lt;OtherPol3&gt; 相似,看起来更清晰。
  • 顺便说一句,using MyConfig1 = Config&lt;&gt;; using MyConfig2 = Config&lt;OtherPol1, DefaultPol2, OtherPol3&gt;; 可能就足够了。
  • @Jarod42 “把它放在配置中”是个好主意,看起来好多了!关于您的第二个建议:当您有 10 个策略时更改最后一个策略将要求您首先重复 9 个默认策略。我想避免这种情况。
  • 我说的是 "might",所以这取决于 OP 的使用情况。 OP 有多少不同的配置,它们之间有多少不同,它们是否以某种方式联系在一起......
【解决方案2】:

使用别名似乎更好:

template <class int_t, class float_t>
using MyShell = Shell<int_t, float_t, MyPol1, MyPol2, ..., MyPolN>;

【讨论】:

  • 我比我更喜欢这个解决方案 :) 不过,这个解决方案只适用于宏,对吧?
  • 不需要 MACRO,只需使用具体和预期的策略定义一次别名。
  • 我明白了!好吧,这是我现在的方法,但我想节省用户编写长模板参数列表(“代码”中的第二行)的需要。我试图弄清楚如何将其作为选项列表来完成(就像我原始帖子中的defaults),而不是模板参数列表(最重要的是,参数的顺序很重要)
  • 写一次using 代码,或者写一次宏列表从用户的角度来看似乎很相似......
  • 也许你是对的,我试图删除“助手”(别名)是一种矫枉过正。我应该再考虑一下:)
猜你喜欢
  • 2012-10-11
  • 2012-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
相关资源
最近更新 更多