C++与D,Ada和Eiffel(模板的可怕错误消息)

Umm*_*mma 25 c++ templates d ada generic-programming

C++的一个问题是我们从密集使用模板和模板元编程的代码中获得的可怕错误消息.这些概念旨在解决这个问题,但遗憾的是它们不会出现在下一个标准中.

我想知道,这个问题是否适用于支持通用编程的所有语言?或者C++模板出了什么问题?

不幸的是,我不知道任何其他支持泛型编程的语言(Java和C#泛型太简单了,没有C++模板那么强大).

所以我问你们:D,Ada,Eiffel模板(仿制药)是否也会产生如此丑陋的错误消息?是否可以使用具有强大通用编程范例的语言,但没有丑陋的错误消息?如果是,这些语言如何解决这个问题?

编辑:为downvoters.我真的很喜欢C++和模板.我不是说模板很糟糕.实际上我是泛型编程和模板元编程的忠实粉丝.我只是问为什么我从编译器那里收到如此丑陋的错误信息.

Mat*_* M. 15

问题的核心是,无论上下文如何,错误恢复都很困难.

当你考虑C和C++可怕的语法时,你只能怀疑错误信息并不比这更糟糕!我担心C语法是由那些对语法基本属性一无所知的人设计的,其中一个就是对语境的依赖越少越好,另一个就是你应该努力制作尽可能明确无误.

让我们说明一个常见错误:忘记分号.

struct CType {
  int a;
  char b;
}
foo
bar() { /**/ }
Run Code Online (Sandbox Code Playgroud)

好的,这是错的,丢失的分号应该去哪里?不幸的是,这是不明确的,它可以在之前或之后去,foo因为:

  • C认为在定义a之后以步幅声明变量是正常的 struct
  • C认为不指定函数的返回类型(在这种情况下默认为int)

如果我们有理由,我们可以看到:

  • 如果foo命名一个类型,那么它属于函数声明
  • 如果没有,它可能表示一个变量...除非我们当然写了一个错字并且它本来是要写的fool,恰好是一个类型:/

正如您所看到的,错误恢复非常困难,因为我们需要推断出作者的意思,并且语法远非接受.但这并非不可能,大多数错误确实可以或多或少地正确诊断,甚至可以从......中恢复......只需要付出相当大的努力.

似乎工作人员对gcc生成快速代码更感兴趣(我的意思是快速,在gcc 4.6上搜索最新的基准测试)并添加有趣的功能(gcc已经实现了大多数 - 如果不是全部 - C++ 0x)而不是生成易于阅读的错误消息.你能怪他们吗?我不能.

幸运的是,有些人认为准确的错误报告和良好的错误恢复是一个非常有价值的目标,其中一些人已经在CLang工作了很长时间,他们仍在继续这样做.

一些不错的功能,在我的头顶:

  • Terse但完整的错误消息,其中包括源范围,以准确显示错误发出的位置
  • Fix-It注意到它的含义很明显
  • 在这种情况下,编译器会解析文件的其余部分,就像修复已经存在一样,而不是在乱码线上喷出线条
  • (最近)避免包括用于笔记的包括堆栈,以便切断
  • (最近)尝试只暴露开发人员实际编写的模板参数类型,并保留typedef(因此谈论std::vector<Name>而不是std::vector<std::basic_string<char, std::allocator<char>>, std::allocator<std::basic_string<char, std::allocator<char>> >使得所有区别)
  • (最近)如果template在从另一个模板方法调用模板方法中丢失的情况下丢失,则正确恢复

但是每个人都需要几个小时几天的工作.

他们当然不是免费的.

现在,概念应该(通常)使我们的生活更轻松.但它们大多未经测试,因此最好将它们从草案中删除.我必须说我为此感到高兴.考虑到C++的相对惯性,最好不要包含尚未彻底修改的功能,概念图并没有让我感到非常激动.他们似乎也没有激动Bjarne或Herb,因为他们说他们将从头开始重新思考下一个标准的概念.

  • "C认为不指定函数的返回类型是正常的(在这种情况下,它默认为int)".然而,C++绝对不允许使用这种语法,而且由于问题涉及C++,我肯定会编辑它. (5认同)
  • 有更糟糕的 C++ 特定歧义。例如,`SomeType Foo(abcd);` 声明的内容无法从语法中推导出来。只有环境给出了正确的答案:如果`abcd` 是类型名称,那么`Foo` 是一个函数,采用`abcd` 类型的参数返回`SomeType`,如果`abcd` 是对象名称,那么Foo 是`SomeType` 类型的对象用 `abcd` 值初始化。长期以来,这一直是 G++ 开发人员的主要问题。以及许多其他类型名称与对象名称问题。无论如何,您的消息很棒。+1 (3认同)
  • `A*B = D+E;`:如果 `A` 是一种类型,那就是减速。如果 `A` 是一个变量,它会重载 `operator*` 以返回重载赋值的东西,那就是表达式求值。 (2认同)

T.E*_*.D. 14

一般来说,我发现用于泛型的Ada编译器错误消息实际上并不比任何其他Ada编译器错误消息更难读.

另一方面,C++模板错误消息因错误小说而臭名昭着.我认为主要的区别在于C++进行模板实例化的方式.问题是,C++模板比Ada泛型更灵活.它非常灵活,几乎就像一个宏预处理器.Boost中的聪明人使用它来实现lambdas甚至整个其他语言之类的东西.

由于这种灵活性,每次首次遇到模板参数的特定排列时,基本上必须重新编译整个模板层次结构.因此,解决了API不兼容问题的问题最终会被提交给糟糕的API客户端进行解密.

在Ada中,泛型实际上是强类型的,并且提供隐藏到客户端的完整信息,就像普通包和子例程一样.因此,如果您收到错误消息,通常只是引用您尝试实例化的通用,而不是用于实现它的整个层次结构.

所以,是的,C++模板错误消息比Ada更差.

现在调试完全是另一回事......

  • C++ 模板是图灵完备的,因此它们不仅仅是一个宏预处理器。 (2认同)

dsi*_*cha 9

D有两个功能可以提高模板错误消息的质量:约束和static assert.

// Use constraints to only allow a function to operate on random access 
// ranges as defined in std.range.  If something that doesn't satisfy this
// is passed, the compiler will error before even trying to instantiate
// fun().
void fun(R)(R range) if(isRandomAccessRange!(R)) {
    // Do stuff.
}


// Use static assert to check a high level invariant.  If 
// the predicate is false, the error message will be 
// printed and compilation will stop before a screen 
// worth of more confusing errors are encountered.
// This function takes any number of ranges to merge sort
// and the same number of temporary buffers to merge into.
void mergeSort(R...)(R ranges) {
    static assert(R.length % 2 == 0, 
        "Must have equal number of ranges to be sorted and temporary buffers.");

    static assert(allSatisfy!(isRandomAccessRange, R), 
        "All arguments to mergeSort must be random access ranges.");

    // Implementation
}
Run Code Online (Sandbox Code Playgroud)

  • C++ 0x有`static_assert`,所以单独的第二部分不再真正使D更好.但结合编译时字符串处理,D's`static assert`的错误报告功能要优越得多(例如,你不能在http:// ideone中显示`q {a**b}`和`int` .com/TQ6wY in C++ 0x). (3认同)

tra*_*god 9

文章泛型编程概述了几种语言,包括许多仿制药的利弊阿达特别.虽然缺少模板专业化,但所有Ada通用 实例都"等同于实例声明......后面紧跟实例主体".实际上,错误消息往往发生在编译时,它们通常表示熟悉的类型安全违规.

  • 此外,Ada源代码更易于阅读.这导致更可读的错误消息. (6认同)

Lot*_*har 5

Eiffel 拥有所有错误消息中最好的,因为它拥有所有模板系统中最好的。它完全集成到语言中并且运行良好,因为它是唯一在参数中使用协变的语言。

因此,它不仅仅是一个简单的编译器复制和粘贴。不幸的是,用几行来解释差异是不可能的。去看看 EiffelStudio。