在我的C ++代码中,它主要依赖于模板元编程,我有类似的东西:
template <int TFoo, int TBar, int TBaz, int TQux, typename TSpam>
struct MyClassConfig {
static int const Foo = TFoo;
static int const Bar = TBar;
static int const Baz = TBaz;
static int const Qux = TQux;
using Spam = TSpam;
};
template <typename Config>
class MyClass {
...
};
也就是说,我使用虚拟类来包含各种参数。这里通常有很多嵌套,所以MyClassConfig::Spam
可能再次成为这样的配置类。
这一切都有效,直到编译器(g ++)决定它不喜欢我的代码。在这一点上,它会很高兴地打印出整个MyClassConfig
。结合使用的其他形式的元编程,错误消息然后爆炸成兆字节。
编辑为了更清楚地了解发生了什么,请参阅my actual code和a typical error output。第一个链接指向我的巨型配置类型别名为PrinterParams
,然后将其作为模板参数提供给PrinterMain<>
。看看错误输出是如何由这种配置类型组成的99%完全被吹灭。我相信如果只有PrinterParams
以别名形式保存而不是由编译器扩展,那么错误将更具可读性。
答案 0 :(得分:3)
使用您可以获得的最新版本的GCC。海湾合作委员会的人员改进了他们的错误信息。特别是4.8格式化它们更好,未来4.9添加颜色。回顾过去,我想知道如何使用GCC 4.4或更早版本。
如果您可以切换到clang,则会有优秀的错误消息。 Clang是GCC改进错误信息的主要推动力。
但在你的情况下,我认为罗伯特哈维是对的。您的示例看起来像模板滥用。
答案 1 :(得分:3)
感谢您致电C ++标准委员会。由于通话量非常大,我们在接听电话时遇到的延迟时间超过正常水平。对于给您带来的不便,我们深表歉意。您的来电对我们很重要。请留在线上。
(滑稽的帽子)
爆炸性错误消息问题过去是,现在仍然是C ++库编写者和用户的主要祸根。没有令人满意的解决方案。 Concepts被认为是C ++ 11中的解决方案,但最后一分钟就刮掉了概念。 Concepts Lite可能会也可能不会进入C ++ 1y。截至目前(C + 11),图书馆作者留下static_assert
,但这需要手工考虑周到。体力劳动,嘘!要使用static_assert
,您,图书馆作家,请执行以下操作:
constexpr
或普通旧type<arg>::value
),当满足要求时,该函数为真。static_assert
声明中使用所有这些函数。虔诚地应用这些程序,您可能有可能产生不会爆炸到兆字节的可读错误消息。
下面是一个匆忙抛出的例子,说明如何做到这一点。用g ++编译它,看看会发生什么。只记得它还没有准备好生产;)没有尝试处理引用,右值引用和const
。请注意,clang ++报告static_assert
和它在函数中找到的所有其他错误,这种错误会使其失败;而g ++仅报告static_assert
个。因此对于clang ++,如果任何静态断言被触发,我们还需要压缩函数体;这是留给读者的练习。
#include <vector>
#include <algorithm>
#include <type_traits>
#include <utility>
template <class T> struct supports_inequal
{
template <class U> static auto test(const U u) -> decltype(bool(u != u), char(0)) { }
static std::array<char, 2> test(...) { }
static const bool value = (sizeof(test(std::declval<T>())) == 1);
};
template <class T> struct supports_dereference
{
template <class U> static auto test(const U u) -> decltype(*u, char(0)) { }
static std::array<char, 2> test(...) { }
static const bool value = (sizeof(test(std::declval<T>())) == 1);
};
template <class T> struct supports_postincrement
{
template <class U> static auto test(U u) -> decltype(u++, char(0)) { }
static std::array<char, 2> test(...) { }
static const bool value = (sizeof(test(std::declval<T>())) == 1);
};
template <class T1, class T2> struct supports_assignment
{
template <class U, class V> static auto postincrement_test(U u, const V v) -> decltype(*u = *v, char(0)) { }
static std::array<char, 2> test(...) { }
static const bool value = (sizeof(test(std::declval<T1>(),std::declval<T2>())) == 1);
};
template <typename It1, typename It2, typename It3>
void my_copy (It1 it1, It2 it2, It3 it3)
{
// Check that It1 and It2 are the same type
static_assert (std::is_same<It1, It2>::value, "\n\n\nArgument 1 and argument 2 of my_copy(it1, it2, it3) must be of the same type\n\n\n");
static const bool previous_assertions_1 = std::is_same<It1, It2>::value;
static_assert (!previous_assertions_1 || supports_inequal<It1>::value, "\n\n\nArgument 1 and argument 2 of my_copy(it1, it2, it3) must be comparable with '!='\n\n\n");
static const bool previous_assertions_2 = previous_assertions_1 && supports_inequal<It1>::value;
static_assert (!previous_assertions_2 || supports_dereference<It1>::value, "\n\n\nArguments 1 and 2 of my_copy(it1, it2, it3) must be dereferenceable\n\n\n");
static const bool previous_assertions_3 = previous_assertions_2 && supports_dereference<It1>::value;
static_assert (!previous_assertions_3 || supports_dereference<It3>::value, "\n\n\nArgument 3 of my_copy(it1, it2, it3) must be dereferenceable\n\n\n");
static const bool previous_assertions_4 = previous_assertions_3 && supports_dereference<It3>::value;
static_assert (!previous_assertions_4 || supports_postincrement<It1>::value, "\n\n\nArguments 1 and 2 of my_copy(it1, it2, it3) must be postincrementable\n\n\n");
static const bool previous_assertions_5 = previous_assertions_4 && supports_postincrement<It1>::value;
static_assert (!previous_assertions_5 || supports_dereference<It3>::value, "\n\n\nArgument 3 and of my_copy(it1, it2, it3) must be postincrementable\n\n\n");
static const bool previous_assertions_6 = previous_assertions_4 && supports_postincrement<It3>::value;
std::copy (it1, it2, it3); // g++ does not complain here when static_assert fires, clang++ does (QoI issue": we have staic assert so that we could control the error messages!)
}
struct A {};
int main ()
{
int *a, *b, *c;
my_copy(a, b, c); // no error
std::copy(a, b, c); // no error
my_copy(a, b, A()); // human readable error message: argument 3 of my_copy(it1, it2, it3) must be dereferenceable
std::copy(a, b, A()); // stack of incomprehensible error messages : "\n\n\nerror: no type named ‘value_type’ in ‘struct std::iterator_traits<A>’"?
}
答案 2 :(得分:0)
更简单的解决方案是使用元函数:
template <int TFoo, int TBar, int TBaz, int TQux, typename TSpam>
struct MyClassConfig { };
template <int TFoo, int TBar, int TBaz, int TQux, typename TSpam>
constexpr int FooOf(MyClassConfig<TFoo, TBar, TBaz, TQux, TSpam>) {
return TFoo;
};
using currentConfig = MyClassConfig<1,2,3,4, float>;
const int currentFoo = FooOf(currentConfig()); // = 1
由于FooOf
不是MyClassConfig
的成员,因此该类保持较小。