【问题标题】:Elegant way to avoid multiple template instantiatons of function that doesn't depend on templated type避免不依赖于模板类型的函数的多个模板实例化的优雅方法
【发布时间】:2014-08-04 22:32:17
【问题描述】:

我有一个看起来像这样的基本类:

template <typename TCode, TCode SuccessVal>
class Error
{
public:
    typedef TCode code_t;

    Error(TCode code, char const *descr="Unknown Error."):
        code(code), descr(descr)
    {

    }

    ...

    char const *description() const
    {
        return descr;
    }

    ...

private:
    TCode code;
    char const *descr;
};

它所做的只是封装某种错误代码枚举类,以便为日志记录提供更多上下文。

现在说我有一个函数panic

template <typename TCode, TCode SuccessVal>
void panic(Error<TCode, SuccessVal> const &e)
{
    puts("Panic!");
    printf("Unrecoverable error: %s", e.description());
    abort();
}

使用 -fdump-tree-original 编译表明,在我的情况下,这会产生几个不同的函数,但代码完全相同。这是您所期望的,但可能不是您所希望的。

一个明显的路线是一个只包含消息的基类和一个接收消息的构造函数,但我觉得这很不吸引人。

我们从不使用错误代码本身,所以我们所做的一切都依赖于 T。如何避免大量编译为基本相同代码的模板实例化?

另一个理想的特性是确保无论 TCode 是什么类型,它都可以强制转换为整数类型。

【问题讨论】:

  • 在 Visual Studio 和其他工具链中,链接器会检测到这些并合并它们。
  • @MooingDuck:说得好!

标签: c++ templates gcc c++11


【解决方案1】:

这段代码的一个明显的因式分解是这样的:

[[noreturn]] void panic_with_message(char const * msg)
{
    std::printf("Panic!\nUnrecoverable error: %s\n", msg);
    std::fflush(stdout);
    std::abort();
}

template <typename T, T Val>
[[noreturn]] static inline void panic(Error<T, Val> e)
{
    panic_with_message(e.description());
}

您可以只将模板与函数声明一起放入标题中,并将函数定义保存在单独的翻译单元中。这应该将代码膨胀降至最低:

// panic.hpp

#ifndef H_PANIC
#define H_PANIC

#include "error.hpp"  // for Error<T, Val>

[[noreturn]] void panic_with_message(char const * msg);

template <typename T, T Val>
[[noreturn]] static inline void panic(Error<T, Val> e)
{
    panic_with_message(e.description());
}

#endif

【讨论】:

  • 我承认通常会为我的模板制作&lt;void&gt; 专业化并为此目的滥用它:(
  • visual c++ 12.0 (2013) 和 g++ 4.8.2 都不支持[[noreturn]]。两个编译器都有 C++11 之前的语言扩展,分别是 __declspec( noreturn )__attribute__((noreturn)),IIRC。因此,为了提供用户可以尝试的示例,此时我认为最好通过使用宏符号来捕获预期的语义,也许还有建议的实现(或解释)。
  • @Cheersandhth.-Alf 你确定g++ 4.8.2不支持[[noreturn]]吗?它至少在 4.8.1 中 does compile 并且这表明在 4.8 中实现了通用属性:gcc.gnu.org/bugzilla/show_bug.cgi?id=53528
  • @user2079303:谢谢。现在,因为它编译,我不太确定。 ;-)
  • @Kerrek SB:嗯,这有点符合我的想法,尽管我希望有一些简洁的模板魔法可用。我对[[noreturn]]不熟悉,我猜它和C11中的_No_return意思是一样的?
【解决方案2】:

另一种选择是使用外部模板声明。 c++11 编译器和较早的 Visual Studio 版本支持这一点(作为语言扩展。)假设您有相当少量的 Error 实例化,您可以为每个错误实例定义一个 extern 模板声明,并将实际的函数实例化放在单个翻译单元。这可确保您对每种恐慌变体只有一个实例化。

panic.hpp

#ifndef PANIC_HPP
#define PANIC_HPP

template <typename TCode, TCode SuccessVal>
struct Error
{
    const char* description() { return "boom!"; }
};

template <typename TCode, TCode SuccessVal>
void panic(Error<TCode, SuccessVal> e);

//! So assume you have Error<ErrorCode, SuccessVal>
//! Place this in the header where the panic function prototype is declared.
//! You'll need one for each type or Error<T,Val> instantiation.
extern template void panic<int, 0>(Error<int, 0> e);
extern template void panic<int, 1>(Error<int, 1> e);
extern template void panic<int, 2>(Error<int, 2> e);

#endif//PANIC_HPP

panic.ipp

#ifndef PANIC_IPP
#define PANIC_IPP

#include "panic.hpp"
#include <cstdio>
#include <cstdlib>

template <typename TCode, TCode SuccessVal>
inline void panic(Error<TCode, SuccessVal> e)
{
    printf("Unrecoverable error: %s scode: %d\n", e.description(), SuccessVal);
    std::abort();
}

#endif//PANIC_IPP

panic.cpp

#include "panic.ipp"

//! Declare the instantiation for each Error<T, Val> which you don't want duplicated.
template void panic<int, 0>(Error<int, 0> e);
template void panic<int, 1>(Error<int, 1> e);
template void panic<int, 2>(Error<int, 2> e);

main.cpp

#include "panic.hpp"

int main()
{
    panic(Error<int, 0>());
    panic(Error<int, 1>());//! These compile/link but aren't called due to abort.
    panic(Error<int, 2>());//! ...
    //panic(Error<int, 3>()); //! won't link due to unresolved symbol as not instantiated.
    return 0;
}

【讨论】:

  • 请注意,这可以与 Kerrek 的答案结合使用以获得更好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
相关资源
最近更新 更多