为什么C ++没有反射? [英] Why does C++ not have reflection?

查看:194
本文介绍了为什么C ++没有反射?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个有点奇怪的问题。我的目标是了解语言设计决策并确定在C ++中反射的可能性。


  1. 为什么C ++语言委员会没有去用语言实现反思?


  2. 如果要实现C ++的反射,那么在一个虚拟机上不能运行的语言挑战?


我猜反射的用法是众所周知的:编辑器可以更容易编写,程序代码将更小,可以为单元测试生成mock等。

解决方案

在C ++中使用反射有几个问题。 / p>


  • 这是一个很大的工作要添加,C ++委员会相当保守,不要花时间在激进的新功能除非他们肯定会回报。 (建议添加一个类似于.NET程序集的模块系统,虽然我认为有一个共识,这将是很好的,它不是他们的最高优先级,目前,并已被推回到很久以后C ++ 0x。此功能的动机是摆脱 #include 系统,但它也将启用至少一些元数据)。


  • 您不支付不是
    的费用。这是C ++基础的基本的
    设计哲学之一。
    为什么我的代码会携带
    元数据,如果我可能永远不需要它?
    此外,添加元数据
    可能会阻止编译器从
    优化。如果我可能永远不需要
    的元数据,我为什么要在我的代码中支付
    的费用?



  • C ++使非常很少保证
    关于编译的代码。
    编译器允许做很漂亮的
    任何它喜欢,只要
    的结果功能是什么
    是预期的。例如,您的
    类实际上不需要
    。编译器可以优化他们,内联
    他们做的一切,它
    经常这样做,因为
    即使简单的模板代码倾向于
    创建相当一些模板
    实例化。 C ++标准
    依赖于这种激进的
    优化。如果
    实例化和破坏
    对象的开销可以被优化,Functor只有
    执行。向量上的
    运算符[] 只与性能
    中的原始
    数组索引相当,因为整个运算符可以是
    因此从编译的代码中完全删除
    。 C#和Java
    对编译器的
    输出提供了很多保证。如果我在C#中定义
    一个类,那么在生成的程序集中将存在

    即使我从来没有使用它。即使所有
    调用它的成员函数可以
    内联。类必须是
    ,所以反射可以找到
    。其中一部分被C#
    编译为字节码,这意味着
    ,JIT编译器可以
    删除
    类定义和inline
    函数,如果它喜欢,即使
    初始C#编译器不能。在C ++中,
    只有一个编译器,它
    必须输出高效的代码。如果你
    允许检查C ++可执行文件的元数据
    ,你会期望
    看到它定义的每个类,其中
    意味着编译器会有$ b $


  • 然后有模板。b。保存所有定义的类,
    即使不必要。
    C ++中的模板不像其他语言中的
    泛型。每个
    模板实例化创建一个
    类型。 std :: vector< int> 是一个完全独立的类
    std :: vector< float> 。这在一个整个
    程序中增加了
    很多不同类型。我们的反映
    应该怎么看? 模板 std :: vector ?但
    怎么可能,因为这是一个
    源代码结构,在运行时没有
    意思?它必须看到
    单独的类
    std :: vector< int>
    std: :vector< float> 。和
    std :: vector< int> :: iterator
    std :: vector< float& / code>,相同
    const_iterator 等。和
    一旦你进入模板
    元编程,你快速结束
    实例化数百个模板,
    所有这些都内联,并由编译器再次删除
    。它们没有
    的意思,除了作为
    编译时元程序的一部分。如果所有的
    这几百个类都是可见的
    到反射?他们必须,
    因为否则我们的反射
    将是无用的,如果它甚至不保证我定义的类将实际上在 。另一个问题是,模板类直到实例化才存在。想象一个使用 std :: vector< int> 的程序。我们的反射系统能否看到 std :: vector< int> :: iterator ?一方面,你一定会期待。这是一个重要的类,它定义在 std :: vector< int> ,元素中存在 。另一方面,如果程序从未实际使用此迭代器类模板,其类型将永远不会被实例化,因此编译器不会首先生成类。


  • 最后,反射不是很重要的
    在C ++中的重要性因为它是在C#。
    的原因又是,模板
    元编程。它不能解决
    的一切,但对于许多情况下,
    否则你会诉诸于
    反射,可以写一个
    元程序,做同样的
    事情在编译时。
    boost :: type_traits 是一个简单的
    示例。你想知道类型
    T ?检查其 type_traits 。在C#中,
    你必须在其
    类型使用反射后绕开。反射
    对于某些
    事情仍然有用(主要用法我可以看到,
    元编程不能轻易
    替换,是为自动生成的
    序列化代码) ,但是
    对于
    C ++来说会带来一些重要的成本,并且它不像其他语言一样频繁。



编辑:
回应评论:



cdleary:
是的,类似的,它们存储关于在可执行文件中使用的类型的元数据。但他们也遭受我描述的问题。如果你曾经试过调试一个发布版本,你会知道我的意思。有很大的逻辑差距,你在源代码中创建了一个类,它在最终代码中已经内联了。如果你使用反射有用的东西,你需要它更可靠和一致。因为它是,类型将消失,几乎每次你编译时消失。你改变一个细微的细节,编译器决定改变哪些类型被内联,哪些类型不作为响应。当您甚至不确定最相关的类型将在元数据中表示时,如何从中提取有用的信息?你在寻找的类型可能在最后的建设,但现在已经走了。明天,有人会检查一个小的无辜的变化,一个小的无辜的功能,这使得类型只是足够大,它不会完全内联,所以它会再回来。这对于调试符号仍然有用,但不会太多。我很讨厌在这些条件下为一个类生成序列化代码。



Evan Teran:当然这些问题可以解决。但这回到我的观点#1。它需要很多工作,C ++委员会有很多他们认为更重要的事情。在C ++中获得一些有限的反射(和它将是有限的)的好处真的足够大,足以说明以牺牲其他功能为代价?是否真的有一个巨大的好处添加功能的核心语言,已经(大部分)通过库和预处理器,如QT的?也许,但是,如果这样的图书馆不存在,需要的不那么紧迫。
对于你的具体建议,但我相信,不允许在模板将使它完全无用。例如,您将无法在标准库中使用反射。什么样的反射不会让你看到 std :: vector ?模板是C ++的一个巨大部分。对模板无效的功能基本上是无用的。



但是你说得对,可以实现某种形式的反射。但这是语言的一个重大变化。就像现在一样,类型只是编译时构造。它们的存在是为了编译器的好处,没有别的。代码编译完成后, 没有类。如果你伸展自己,你可以认为函数仍然存在,但真的,所有有一堆跳转汇编指令,和很多堆栈push / pop的。



但是像我说的,有一个建议是修改编译模型,添加自包含的模块,存储选择类型的元数据,允许其他模块引用它们,而不必混淆 #include 。这是一个好的开始,老实说,我感到惊讶的是,标准委员会不只是把建议放在太大的变化。那么也许在5-10年? :)


This is a somewhat bizarre question. My objectives are to understand the language design decision and to identify the possibilities of reflection in C++.

  1. Why C++ language committee did not go towards implementing reflection in the language? Is reflection too difficult in a language that does not run on a virtual machine (like java)?

  2. If one were to implement reflection for C++, what will be the challenges?

I guess the uses of reflection are well-known: editors can be more easily written, program code will be smaller, mocks can be generated for unit tests and so on. But it would be great if you could comment on uses of reflection too.

解决方案

There are several problems with reflection in C++.

  • It's a lot of work to add, and the C++ committee is fairly conservative, and don't spend time on radical new features unless they're sure it'll pay off. (A suggestion for adding a module system similar to .NET assemblies has been made, and while I think there's general consensus that it'd be nice to have, it's not their top priority at the moment, and has been pushed back until well after C++0x. The motivation for this feature is to get rid of the #include system, but it would also enable at least some metadata).

  • You don't pay for what you don't use. That's one of the must basic design philosophies underlying C++. Why should my code carry around metadata if I may never need it? Moreover, the addition of metadata may inhibit the compiler from optimizing. Why should I pay that cost in my code if I may never need that metadata?

  • Which leads us to another big point: C++ makes very few guarantees about the compiled code. The compiler is allowed to do pretty much anything it likes, as long as the resulting functionality is what is expected. For example, your classes aren't required to actually be there. The compiler can optimize them away, inline everything they do, and it frequently does just that, because even simple template code tends to create quite a few template instantiations. The C++ standard library relies on this aggressive optimization. Functors are only performant if the overhead of instantiating and destructing the object can be optimized away. operator[] on a vector is only comparable to raw array indexing in performance because the entire operator can be inlined and thus removed entirely from the compiled code. C# and Java make a lot of guarantees about the output of the compiler. If I define a class in C#, then that class will exist in the resulting assembly. Even if I never use it. Even if all calls to its member functions could be inlined. The class has to be there, so that reflection can find it. Part of this is alleviated by C# compiling to bytecode, which means that the JIT compiler can remove class definitions and inline functions if it likes, even if the initial C# compiler can't. In C++, you only have one compiler, and it has to output efficient code. If you were allowed to inspect the metadata of a C++ executable, you'd expect to see every class it defined, which means that the compiler would have to preserve all the defined classes, even if they're not necessary.

  • And then there are templates. Templates in C++ are nothing like generics in other languages. Every template instantiation creates a new type. std::vector<int> is a completely separate class from std::vector<float>. That adds up to a lot of different types in a entire program. What should our reflection see? The template std::vector? But how can it, since that's a source-code construct, which has no meaning at runtime? It'd have to see the separate classes std::vector<int> and std::vector<float>. And std::vector<int>::iterator and std::vector<float>::iterator, same for const_iterator and so on. And once you step into template metaprogramming, you quickly end up instantiating hundreds of templates, all of which get inlined and removed again by the compiler. They have no meaning, except as part of a compile-time metaprogram. Should all these hundreds of classes be visible to reflection? They'd have to, because otherwise our reflection would be useless, if it doesn't even guarantee that the classes I defined will actually be there. And a side problem is that the template class doesn't exist until it is instantiated. Imagine a program which uses std::vector<int>. Should our reflection system be able to see std::vector<int>::iterator? On one hand, you'd certainly expect so. It's an important class, and it's defined in terms of std::vector<int>, which does exist in the metadata. On the other hand, if the program never actually uses this iterator class template, its type will never have been instantiated, and so the compiler won't have generated the class in the first place. And it's too late to create it at runtime, since it requires access to the source code.

  • And finally, reflection isn't quite as vital in C++ as it is in C#. The reason is again, template metaprogramming. It can't solve everything, but for many cases where you'd otherwise resort to reflection, it's possible to write a metaprogram which does the same thing at compile-time. boost::type_traits is a simple example. You want to know about type T? Check its type_traits. In C#, you'd have to fish around after its type using reflection. Reflection would still be useful for some things (the main use I can see, which metaprogramming can't easily replace, is for autogenerated serialization code), but it would carry some significant costs for C++, and it's just not necessary as often as it is in other languages.

Edit: In response to comments:

cdleary: Yes, debug symbols do something similar, in that they store metadata about the types used in the executable. But they also suffer from the problems I described. If you've ever tried debugging a release build, you'll know what I mean. There are large logical gaps where you created a class in the source code, which has gotten inlined away in the final code. If you were to use reflection for anything useful, you'd need it to be more reliable and consistent. As it is, types would be vanishing and disappearing almost every time you compile. You change a tiny little detail, and the compiler decides to change which types get inlined and which ones don't, as a response. How do you extract anything useful from that, when you're not even guaranteed that the most relevant types will be represented in your metadata? The type you were looking for may have been there in the last build, but now it's gone. And tomorrow, someone will check in a small innocent change to a small innocent function, which makes the type just big enough that it won't get completely inlined, so it'll be back again. That's still useful for debug symbols, but not much more than that. I'd hate trying to generate serialization code for a class under those terms.

Evan Teran: Of course these issues could be resolved. But that falls back to my point #1. It'd take a lot of work, and the C++ committee has plenty of things they feel is more important. Is the benefit of getting some limited reflection (and it would be limited) in C++ really big enough to justify focusing on that at the expense of other features? Is there really a huge benefit in adding features the core language which can already (mostly) be done through libraries and preprocessors like QT's? Perhaps, but the need is a lot less urgent than if such libraries didn't exist. For your specific suggestions though, I believe disallowing it on templates would make it completely useless. You'd be unable to use reflection on the standard library, for example. What kind of reflection wouldn't let you see a std::vector? Templates are a huge part of C++. A feature that doesn't work on templates is basically useless.

But you're right, some form of reflection could be implemented. But it'd be a major change in the language. As it is now, types are exclusively a compile-time construct. They exist for the benefit of the compiler, and nothing else. Once the code has been compiled, there are no classes. If you stretch yourself, you could argue that functions still exist, but really, all there is is a bunch of jump assembler instructions, and a lot of stack push/pop's. There's not much to go on, when adding such metadata.

But like I said, there is a proposal for changes to the compilation model, adding self-contained modules, storing metadata for select types, allowing other modules to reference them without having to mess with #includes. That's a good start, and to be honest, I'm surprised the standard committee didn't just throw the proposal out for being too big a change. So perhaps in 5-10 years? :)

这篇关于为什么C ++没有反射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆