【问题标题】:Where and When should we use compile time macro concatenation?我们应该在何时何地使用编译时宏连接?
【发布时间】:2015-08-03 07:07:50
【问题描述】:

我看过这段我认为很棒的代码,因为它使我免于重新编写 getter 成员函数。

#define GET(n_,N_)                      \
        template<typename T>            \
        const T &get##N_() const        \
        {                               \
            return n_;                  \
        }                               \

据我所知,宏在编译时“编写”代码,因此在每个类中注入函数模板,并且因为它是一个模板,它可以补偿任何类型。 所以我最终得到了这个:

class Foo
{
   int m_a;
   GET(m_a,A)
};

然后我是这样使用的:

std::cout<< foo->getA<int>() <<std::endl;

我的问题是,我们应该在何时何地使用宏模板? 有没有办法在调用 getA 成员函数时不指定类型?这是因为它在不同的命名空间中吗?

【问题讨论】:

  • 我永远不会投票。
  • “宏模板”是什么意思?似乎您正在卷积两个完全不相关的概念(宏和模板)。您可以使用宏来“生成”getter 和 setter 函数,但老实说,我不明白为什么它们应该是 template 函数。
  • @顺磁性羊角面包 你使用模板来创建一个通用函数。您使用宏来保存代码。
  • @顺磁牛角面包因为这意味着您可以将这个宏用于您编写的任何课程!并且类通常包含其他东西,而不仅仅是 int。这只是一个例子。如果是 vec3 或者矩阵或者其他用户定义的类型呢?
  • 您在调用它时必须指定类型,这比编写函数的次数要多,所以我预测净节省,以击键次数衡量, 对这个技巧是负面的。此外,如果您经常编写如此多的 getter 以至于它成为一件苦差事,那么您做错了(tm)。

标签: c++ function templates macros getter


【解决方案1】:

正如您所观察到的,我建议将宏和模板混合使用会导致您发现两者的弱点。函数的模板返回类型永远无法推断,因此您始终必须指定它。但幸运的是,有一个解决方案,它涉及将类型拼写到您的宏中:

#define GETTABLE_ATTR(type, name) \
private:
    type name_; \
public:
    type get_##name() const \
    { \
        return name_; \
    }

使用这个宏是否是一个好主意仍然是主观的 - 请记住,您只编写一次代码,并且最好以一种既能防止编写错误又能维护代码的方式编写代码最简单的。

【讨论】:

  • 我以前从未见过这样的东西……很有趣。但是我认为,如果您在类定义中使用它,那么您将遇到一些非常严重的内存对齐问题!此外,您对可能导致歧义错误的重复问题持开放态度。最后,注释是,如果要从类中返回成员数据,则应将其设为 const 两种方式,例如: const type get_##name() const.
  • @Beer o' clock const 对值返回类型无效,实际上会在 g++ 中生成警告。
  • 不,不是!其实这是正确的做法!在 Effective C++ p. 中也提到了这一点。 125!!!他给出的例子是: const T &getSomething() const;
  • @Beer o'clock 我认为您混淆了按值返回和按引用返回。通过引用返回时,添加const 会改变含义和常量正确性是正确的。当我的宏按值返回时,它没有任何效果。
  • 是的,你是对的!如果您要返回一个副本,则返回类型之前的 const 没有任何意义。但是当你返回一个参考时,你需要它——这就是我的困惑。 :)
【解决方案2】:

正如 c++14 所说,仍然需要涉及 c 宏。

我有两个非常常用的宏与 MTP 结合来获得一个编译时间常数,它告诉我一个方法或属性是否存在于一个类中。这只需要函数的名称,该名称不能作为模板的参数给出。所以我在一个 c 宏中准备了它,它“写入”了我的模板,然后可以在 enable_if 子句中使用。

我个人不喜欢“自动吸气剂”中的你的想法,但这只是一个品味问题。

一如既往地在编程中:如果事情有帮助,不是“未定义的行为”,在代码中有很好的记录,并且不能以更严格的方式完成,允许使用 c 宏。对我来说,c 宏是对未集成语言特性的一种“自卫”。

另一个流行的例子是枚举关联文本以进行某种反射或序列化。

捕捉方法存在的例子:

#define DECLARE_HAS_METHOD_FULL_SIGNATURE(NAME) \
template<typename, typename T>\
struct has_method_full_signature_ ## NAME {\
static_assert(\
        std::integral_constant<T, false>::value,\
        "Second template parameter needs to be of function type.");\
};\
\
\
template<typename C, typename Ret, typename... Args>\
struct has_method_full_signature_ ## NAME <C, Ret(Args...)> {\
        template<typename T>\
        static constexpr auto check(T*)\
        -> typename\
        std::is_same<\
        decltype( std::declval<T>(). NAME ( std::declval<Args>()... ) ),\
        Ret   \
        >::type;  \
        \
        template<typename>\
        static constexpr std::false_type check(...);\
        \
        using type = decltype(check<C>(0));  \
        static constexpr bool value = type::value;\ 
}

编辑:在此处添加一些示例代码如何使用此 c-macro 内容。

 #include <utility>
#include <iostream>
#include "component/mytypes_traits.h"

DECLARE_HAS_METHOD_PARMS_ONLY(funny);

DECLARE_HAS_METHOD_FULL_SIGNATURE(f1);
DECLARE_HAS_METHOD_FULL_SIGNATURE(f2);
DECLARE_HAS_METHOD_FULL_SIGNATURE(f3);


class A { public: void funny() {} };

class B { public: void dummy() {} };

class C
{   
    public:
    int f1(int) { return 1;} 
    float f2(int,int) {return 2.0;}
    int f3() { return 1;} 
};  

int main()
{   
    std::cout << has_method_parms_only_funny<A>::value << std::endl;
    std::cout << has_method_parms_only_funny<B>::value << std::endl;
    std::cout << "--" << std::endl;

    std::cout << has_method_full_signature_f1< C, int()>::value << std::endl;
    std::cout << has_method_full_signature_f1< C, int(int)>::value << std::endl;
    std::cout << has_method_full_signature_f1< C, int(int,int)>::value << std::endl;
    std::cout << "--" << std::endl;

    std::cout << has_method_full_signature_f2< C, float()>::value << std::endl;
    std::cout << has_method_full_signature_f2< C, float(int)>::value << std::endl;
    std::cout << has_method_full_signature_f2< C, float(int,int)>::value << std::endl;
    std::cout << "--" << std::endl;

    std::cout << has_method_full_signature_f3< C, int()>::value << std::endl;
    std::cout << has_method_full_signature_f3< C, int(int)>::value << std::endl;
    std::cout << has_method_full_signature_f3< C, int(int,int)>::value << std::endl;
    std::cout << "--" << std::endl;
}   

【讨论】:

  • 您的has_method_full_signature 主模板格式不正确,因为没有有效的专业化。只是不要为它提供定义。
  • 我相信你错了。 check(...) 是错误的部分,而 check(T*) 评估为好的路径。正如我在回答中所写,此类代码应该有据可查。在提供的情况下它不是,所以我必须再次检查它。谢谢:-)
  • 不是Ret(Args...) 的特化,是static_assert 的主要模板。
  • @Barry:我现在为我的宏写了一个简单的测试,它看起来对我来说没问题。也许您可以解释更多细节,或者我们应该提出一个新问题,因为我们在这里的 OP 问题有点过时了。但我希望我能抓住你的论点。也许我的代码被破坏了,所以我想得到任何帮助!
  • @Klaus 感谢您的代码示例。我还没有用 c++14 编码,但我明白了。我只想评论“不能以更严格的方式完成”。有时直接的方式可能意味着代码膨胀——尤其是在模板中。例如,当您为提升特征类编写几个部分类模板特化时,宏可以节省一天的时间!我相信没有关于何时何地使用宏的规则。最终取决于应用和设计。
猜你喜欢
  • 2020-10-16
  • 2014-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-12
  • 2019-11-28
  • 2013-02-24
相关资源
最近更新 更多