6 ms·
Reading C++ code always gives me a weird feeling something is inherently wrong with the language. Header files containing nearly all the implementation because
by arianvanp 14y ago
Reading C++ code always gives me a weird feeling something is inherently wrong with the language. Header files containing nearly all the implementation because that's the only place where you can define templates is so odd... (https://code.google.com/p/cpp-btree/source/browse/btree.h https://code.google.com/p/cpp-btree/source/browse/btree.h)
- guilloche 14y agoagreed.
- blablabla123 14y agoThanks to Comeau C++'s standard compliance, you can put the template into the .cc files. ;)
- KMag 14y agoI think the complaint is more about the C++ compiler needing to expand the template every place it's used, as opposed to Java/C# generics and OCaml functors (functions taking modules as arguments and returning modules as values) which necessitate a pointer indirection at runtime (which a VM/JIT/smart linker may or may not be sophisticated enough to optimize away) but allow the call site to be ignorant of the implementation details at compile time. There are workarounds in C++, but they're less elegant. Arguably, the decision to keep using the C linker and use name mangling and template expansion in the compiler instead of making the linker smarter (or adding a phase between compilation/assembly and linking) was a mistake. Some kind of object file pre-processor that performed name mangling and template expansion on object files probably would have still been necessary, at least in the early years. Certainly incremental compilation and linking would be easier if template expansion were performed on object code (or a low-level intermediate representation) rather than source code.
- cbsmith 14y agoActually, C#'s generics don't require a pointer indirection. It works almost like a hybrid between C++'s templates and Java's Generics.
- cbsmith 14y ago> Arguably, the decision to keep using the C linker and use name mangling and template expansion in the compiler instead of making the linker smarter (or adding a phase between compilation/assembly and linking) was a mistake. I don't know that it was a mistake per se, but this problem with template compilation overhead is definitely a seemingly inevitable consequence. > Certainly incremental compilation and linking would be easier if template expansion were performed on object code (or a low-level intermediate representation) rather than source code. This is effectively what happens... it just happens at compile time instead of link time. Given this template: <bar.hpp> template <typename foo> foo bar(foo x) { return foo; } a compiler is asked to compile: #include <bar.hpp> //source code is parsed and intermediate representation generated by the compiler int x = bar(some_int); //performs template expansion on said object code float y = bar(some_y); //performs template expansion on said object code //bar.hpp's source is examined only once! This is why you can't do some things in templates that you can do with say, C macros (and vice-versa).
- shawn-butler 14y agoI think Sun's compiler also was compliant.
- cbsmith 14y agoEven the EDG guys who wrote that support for the export keyword feel like it was a really, really bad thing: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n142...
- cpeterso 14y agoThe template export feature was removed in C++11 (though the keyword itself is still reserved for potential future use).
- halayli 14y agoWelcome to templates and inline functions.
- viraptor 14y agoComments like this one don't help either: // Inside a btree method, if we just call swap(), it will choose the // btree::swap method, which we don't want. And we can't say ::swap // because then MSVC won't pickup any std::swap() implementations. We // can't just use std::swap() directly because then we don't get the // specialization for types outside the std namespace. So the solution // is to have a special swap helper function whose name doesn't // collide with other swap functions defined by the btree classes.
- zanny 14y agoI don't get whats wrong. They have an implementation of swap in their namespace already they don't want to use, the standard one isn't templated to work with their btree, so you use a different swap in methods. Sounds more like this should be btree::swap and whatever they have as swap() should be something else.
- viraptor 14y agoI think the syntax gymnastics needed to achieve the result are quite complex for what they aim for. There's nothing technically wrong with it. It's just that choosing a function from a namespace shouldn't need a 7 line comment about how and why in my opinion. It's a single statement function. Additionally `using std::swap; swap();` not being exactly equivalent to `std::swap();` is rather strange (even if it can be explained). A candidate for code/design smell in my opinion.
- lbrandy 14y ago> I think the syntax gymnastics needed to achieve the result are quite complex for what they aim for. There's nothing technically wrong with it. It's just that choosing a function from a namespace shouldn't need a 7 line comment... It's probably important to point out that your last sentence above is not correct. That's not what this code is doing. If all it needed to do was call swap from a particular namespace, it would be as simple as you imply. It's trying to maximize the caller's ability to specialize swap for the type he's using in the template, while still allowing for std::swap to be used as a fallback. > Additionally `using std::swap; swap();` not being exactly equivalent to `std::swap();` is rather strange (even if it can be explained). One says "use standard swap". One says "bring std::swap into this namespace and choose the best swap". I agree with you the difference is subtle and non-obvious. But it is important when doing sufficiently generic programming.
- shawn-butler 14y agoI would suggest if you have this feeling that you don't have a good grasp on metaprogramming. This link might help: http://www.parashift.com/c++-faq-lite/templates-defn-vs-decl.html http://www.parashift.com/c++-faq-lite/templates-defn-vs-decl... Also the use of extern keyword for templates which is valid according to the standard is limited by compiler compliance. Kind of a chicken-egg problem, not a language problem. Finally you might investigate the extern template keyword in c++11: http://en.wikipedia.org/wiki/C++11#Extern_template http://en.wikipedia.org/wiki/C++11#Extern_template I have always wondered whether a grand rethinking of compilers/linkers wasn't in order given the radically different face of computing and computational resources available now as opposed to 30+ years ago.
- bo1024 14y ago> I would suggest if you have this feeling that you don't have a good grasp on metaprogramming. I don't think someone has to be an expert on metaprogramming to recognize the flaws in C++ (although I do think it helps).
- fulafel 14y agoor know c++ to be an expert in metaprogramming.
- loup-vaillant 14y agoSince you mention the FAQ, I just have to mention the FQA: http://www.yosefk.com/c++fqa/templates.html#fqa-35.12 http://www.yosefk.com/c++fqa/templates.html#fqa-35.12 More seriously, the primary use of template is not meta programming, but good old parametric polymorphism (or «genericity»). They are more capable than that, but only by accident. https://en.wikipedia.org/wiki/Turing_tarpit https://en.wikipedia.org/wiki/Turing_tarpit If you want actual meta programming, look at Haskell combinator libraries, Lisp macros, and external DSLs (in ascending order of "meta").
- cbsmith 14y agoYes, it is a flawed language to have a declaration and the definition are all in one place together. What other language does this aside from Ruby, Python, Perl, JavaScript, Java, C#, Smalltalk, VisualBasic, PHP, Haskell, Lua...
- fauigerzigerk 14y agoNone of these other languages requires code to be compiled each and every time it is imported/included.
- cbsmith 14y agoThat's really an implementation issue, not a language issue. A C++ interpreter (http://root.cern.ch/drupal/content/cint http://root.cern.ch/drupal/content/cint) for example, doesn't process the includes any more than most of these others. Most modern C++ compilers of course have to parse the file, but do not actually compile code unless it is used. The more sophisticated ones can usually cleverly avoid even parsing a file a second time. Technically most of those languages some degree of processing of those import/includes. Much to my chagrin Python actually evaluates the entire file, as does Perl, Ruby, PHP & JavaScript. Java, Haskell and C# (and I guess now VB) do their static type checking (often very efficiently because the type information is cached or at least already byte compiled), and Lua does a fair bit of processing as well. Smalltalk is image based, so it's kind of a very different context, but it probably could make the best case for "not having to process every time a module is imported/included", because code is loaded in to a global symbol table once, and most implementations wait until message dispatch to resolve things further. In general, I'd say the nature of your concern is more about long compile times, and C++ definitely is "impressive" in that regard. Some of that problem lies within the language's idiosyncrasies (sticking with the legacy C linker being a big one), but a lot of it is simply that it tries to resolve so much at compile time rather than run time. While not always desirable, I think there is definitely a place for tool like that.
- fauigerzigerk 14y agoYes, compile times are my main concern. It is an implementation issue, just like the fact that humans die. It doesn't have to be that way, does it? :-) The other concern is that you have to first find and then change code in two locations most of the time. On top of that, function signatures in the header are different than those in the implementation file, so you can't just copy them. This last issue is a language issue and it is utterly ridiculous. There is no sane reason why you're not allowed to leave virtual, static or parameter default values in the code when you copy it into the implementation file. [Edit] One more thing: If a language is designed in a way that makes it practically impossible to create a fast implementation, that is a language issue in my view. Doesn't mean that such a language has no place. It does and I'm using it.