News for hackers. Stuff that matters.
Re:The C++ Build Process Explained by amelius on Wednesday November 14, 2018 @05:14PM (#18454628)
Aside, C++ would have been so much nicer without the need for header files ...
Re:The C++ Build Process Explained by muricula on Wednesday November 14, 2018 @07:36PM (#18455758)
Headers allow you to ship a binary without the full source code. If you want to build a linux kernel module you don't need all of the linux sources, just the headers.
Re:The C++ Build Process Explained by fermienrico on Wednesday November 14, 2018 @08:00PM (#18455937)
In principle you're right, but the notion that header file exposes just the "interface" is completely false. Class definition, private variables and functions, etc. are all exposed in header file.

Header files are not a way to only expose the interface. You give up a lot more in C++.

I've never had to deal with pesky header files until I started developing C and it immediately struct me as a royal pain in the ass. Even after couple of years of developing C/C++, I find the whole concept of header files archaic. Include preprocessor directive literally copy pastes stuff with no intelligence what-so-ever. The user is now burdened to ensure #includes are guarded to what I call a patchy half-baked hacked up solution - #IFDEF/#DEFINE/#ENDIF and #pragma in C++.

It should be handled automatically by the compiler/preprocessor or IDE and I believe it is now being addressed in the C++17/20 spec with the advent of "modules". This thing should have been written up way back in 1989.

Re:The C++ Build Process Explained by jlarocco on Wednesday November 14, 2018 @08:06PM (#18455980)
> In principle you're right, but the notion that header file exposes just the "interface" is completely false.

Sorry, but it is not "completely false". Doing it properly requires a carefully designed interface to hide internal data structures, and splitting out the end user headers from the internal headers, but it works.

> I've never had to deal with pesky header files until I started developing C and it immediately struct me as a royal pain in the ass.

It's like saying, "I've never had to deal with pesky .py files until I started developing Python."

Re:The C++ Build Process Explained by fnrslvr on Wednesday November 14, 2018 @08:28PM (#18456112)
> Doing it properly requires a carefully designed interface to hide internal data structures, and splitting out the end user headers from the internal headers, but it works.

Out of curiosity, how? (If pointers or other mechanisms for memory indirection are allowed then it's pretty easy, so let's agree to ban those.)

Not a rhetorical question, despite the parenthetical.

Re:The C++ Build Process Explained by cbuq on Wednesday November 14, 2018 @09:18PM (#18456332)
For global or namespace functions you can hide the implementation in the .cpp file.

Doing the same with objects require pointers to hide the details of any private members. A common pattern is called Pointer to IMPLemenation (PIMPL).

Not sure why we need to exclude pointers?

In c++14 this is very much simplified with the use of unique pointers.

std::unique_ptr<Foo> make_foo(...);

Re:The C++ Build Process Explained by fnrslvr on Wednesday November 14, 2018 @10:14PM (#18456590)
Because I want full encapsulation without giving up by-value semantics. I want my clients to be able to take objects of some opaque record type that I've defined, and put them entirely on the stack, or in some contiguous block of memory entirely of their making, without ever getting to know the constituent fields of the record. I want it all, I want to have my cake and eat it too.

When the parent (er, great grandparent?) says their parent is wrong to deny that headers only reveal the interface, I think it's a little disingenuous to base that on an unrevealed assumption that PImpl is in play. PImpl is basically the the idiom that begot Java. One reason I might opt for C++ over Java, is a desire for finer control over the location of memory -- but it's important for me to know that to actually get that, I'll probably have to sacrifice information hiding. It's a trade-off. Yes, on some level it's better to have the option to make that trade-off, but the product here is "encapsulation or value semantics", not "encapsulation and value semantics".

Mind, I'm not a C++ developer. Maybe these days link-time heroic optimization makes the "right" decisions and collapses these kinds of indirections in all the sorts of situations you'd want it to. I write a lot more Java, and my understanding is that HotSpot gets up to a lot of heroics pertaining to this stuff these days -- I've noticed HotSpot will churn through a workload involving processing a collection of records far more quickly if you can arrange for it to stream through an array, even if you'd expect the records to be scattered randomly throughout memory.

Re:The C++ Build Process Explained by nchie on Thursday November 15, 2018 @04:14AM (#18457940)
Well, there is a way to have them on the stack even with PImpl: http://www.gotw.ca/gotw/028.htm

However I'd say it's an ugly hack which should only ever be used if you _really_ need the performance.

Re:The C++ Build Process Explained by ryl00 on Wednesday November 14, 2018 @11:02PM (#18456768)
> I want it all, I want to have my cake and eat it too.

Conceptually this should be possible with some preprocessor/header tinkering; something like:

private.h:

  struct Bar {
    double x;
    double y;
  };
  #define BAR_DEFINED
public.h:

  #ifndef BAR_DEFINED
  struct Bar {
    char opaque[16];
  };
  #endif

  struct Foo {
    Bar bar;
  };
consumer.cpp:

  #include "public.h"
private_implementation.cpp:

  #include "private.h"
  #include "public.h"
Re:The C++ Build Process Explained by blt on Thursday November 15, 2018 @03:05AM (#18457659)
this code can crash on platforms where unaligned access is not allowed. you need an alignas on public Bar. and of course the catastrophic bugs if the size of opaque is not kept in sync properly. the point is: the module system should be doing something like this for you behind the scenes.
Re:The C++ Build Process Explained by jcelerier on Thursday November 15, 2018 @05:47AM (#18458304)
> I want my clients to be able to take objects of some opaque record type that I've defined, and put them entirely on the stack

if you want to put the object on the stack, the compiler has to know the size of the object to reserve enough space on the stack. How can it know the size of the object if it does not have its full definition somewhere ?

Re:The C++ Build Process Explained by pjmlp on Thursday November 15, 2018 @07:11AM (#18458604)
Module symbol table for example, where only the compiler can actually see the complete information about a type, although the consumer code can only access what is exposed as public.
Re:The C++ Build Process Explained by gpderetta on Thursday November 15, 2018 @09:27AM (#18459432)
This is fine if you can recompile a program against a new version of the library, but if you just want to relink it doesn't work well. This is actually while common when a .so is replaced with a newer version in a system without rebulding the world.

Some languages have, like ADA I think, have first class support for runtime sized, stack allocated types, so it might work there.

Re:The C++ Build Process Explained by jchb on Wednesday November 14, 2018 @10:24PM (#18456639)
> Not sure why we need to exclude pointers?

Sometimes you need to. You cannot entirely stack allocate an object that uses PIMPL. Also you cannot allocate an array of such objects compactly in memory.

On the other hand, if you want to be able to evolve the class member variables but still maintain a stable ABI, you need to hide the memory layout, for example with PIMPL. But this is a C++ limitation. For example Objective-C* (and also soon Swift) allows modifying the class layout, adding properties etc, without changing the ABI.

https://en.wikipedia.org/wiki/Objective-C#Non-fragile_instan...

Re:The C++ Build Process Explained by nchie on Thursday November 15, 2018 @04:18AM (#18457958)
> Sometimes you need to. You cannot entirely stack allocate an object that uses PIMPL.

You actually can, if I'm not misunderstanding: http://www.gotw.ca/gotw/028.htm

Re:The C++ Build Process Explained by beached_whale on Wednesday November 14, 2018 @11:38PM (#18456902)
They must have some level of indirection in the resulting code to accomplish that like virtual inheritance. There is a price and this can be done in C++ too, but you have to opt into the cost.
Re:The C++ Build Process Explained by jcelerier on Thursday November 15, 2018 @05:49AM (#18458315)
> Under the modern runtime, an extra layer of indirection is added to instance variable access,

so it's just syntax sugar for PIMPL.

Re:The C++ Build Process Explained by jchb on Thursday November 15, 2018 @09:39AM (#18459515)
Actually not, it works a bit differently. In Objective-C 1, there was no indirection and one had to explicitly use the equivalent of PIMPL to hide private members from the header or avoid the fragile base class problem.

In Objective-C 2 the object meta-data contains a table of instance variable offsets. The dynamic linker can modify this table at load time so you can freely add both instance variables and methods to new revisions of a class.

So what is the deal? Well, when the holder object itself is heap allocated, pimpl is inefficient because every access will require dereferencing two pointers. Also you cannot put protected or virtual members in the internal pimpl class (then there would be no point to have those in the first place).

That being said, it is not like Objective-C is some pinnacle of performance - you cannot allocate objects on the heap, and the compiler doesn't perform any devirtualization. So for performance critical code you have to drop down to C or...C++ :)

Re:The C++ Build Process Explained by creatornator on Wednesday November 14, 2018 @08:59PM (#18456252)
You can split "secret" data structures into another header, and them simply not give that secret header to the customer. If you give the binary and a header with some structures missing, the user can still use whatever you did give them.
Re:The C++ Build Process Explained by jlarocco on Wednesday November 14, 2018 @08:43PM (#18456185)
> Out of curiosity, how? (If pointers or other mechanisms for memory indirection are allowed then it's pretty easy, so let's agree to ban those.)

Here's one popular way to do it in C++: https://en.cppreference.com/w/cpp/language/pimpl

I guess my tongue in cheek response to your parenthetical would be to ban the use of hidden internal data structures ;-)

Re:The C++ Build Process Explained by fermienrico on Wednesday November 14, 2018 @08:08PM (#18455993)
How do you compare .py files with .h files? I am confused as to how you are drawing this analogy.
Re:The C++ Build Process Explained by jlarocco on Wednesday November 14, 2018 @08:29PM (#18456116)
I mean header files are C and C++ source files, so saying you didn't have to deal with them until using C and C++ is a tautology. Likewise, you won't have to deal with .py files until you're using Python.
Re:The C++ Build Process Explained by kccqzy on Thursday November 15, 2018 @01:00AM (#18457221)
> In principle you're right, but the notion that header file exposes just the "interface" is completely false. Class definition, private variables and functions, etc. are all exposed in header file.

If you don't want to expose class internals, just use the PIMPL idiom. It's an extra indirection to protect your own abstraction, so naturally C++ decides to opt for performance by default.

Re:The C++ Build Process Explained by klodolph on Thursday November 15, 2018 @05:21AM (#18458200)
That’s an annoying workaround at best. It’s not necessary in C. It wouldn’t be necessary in C++ if you could declare incomplete class types but still declare their member functions. This would have zero performance impact and improve encapsulation.
Re:The C++ Build Process Explained by jcelerier on Thursday November 15, 2018 @05:45AM (#18458298)
> It’s not necessary in C

sorry what ? most C libraries I know hide their implementations behind opaque types such as `typedef void* my_handle_t`.

Re:The C++ Build Process Explained by klodolph on Thursday November 15, 2018 @10:13AM (#18459823)
Right, I'm saying that's not necessary, you can just do this:

    struct my_type;
    struct my_type *my_type_new(void);
This is more common than `typedef void*` in my experience, because it actually provides some minimal amount of type safety.

You can also do this in C++ of course, but you can't use member functions this way. My real complaint here is that the Pimpl idiom in C++ is more cumbersome than a simple forward declaration and free functions, which is available in C.

Re:The C++ Build Process Explained by de_watcher on Thursday November 15, 2018 @02:35AM (#18457550)
> Include preprocessor directive literally copy pastes stuff with no intelligence what-so-ever.

Well, it's easy to reason about them. The thinking goes about the translation units.

The real problem is that the actual interface is not enforceable: the symbols in the binaries are just names.

Re:The C++ Build Process Explained by bunderbunder on Wednesday November 14, 2018 @08:41PM (#18456171)
> Headers allow you to ship a binary without the full source code.

100% agree. That said, that doesn't mean that they're the only way to do it, or even the best way.

Re:The C++ Build Process Explained by tjoff on Wednesday November 14, 2018 @05:56PM (#18455009)
In principle I agree. An IDE should be able to automatically generate something like a header file regardless of language.

But still, no IDE or tool I've seen does this better. A header file gives you a very nice overview, and it really isn't any hassle to speak of to keep them up to date.

It takes getting used to (as with everything when starting out with a new language), but before you know it you might even start to miss them in other languages.

Re:The C++ Build Process Explained by pcwalton on Wednesday November 14, 2018 @08:18PM (#18456045)
Header files absolutely are a hassle to keep up to date. Besides, they're a terrible experience. You have to pay attention to include order! You have to write forward declarations! You have to write include guards, in 2018!

They're also bad as an overview. They may have been good in the 1980s, but nowadays a proper documentation generator gives you better formatting, search features, and cross-references. Inline documentation is particularly obnoxious in header files: I like seeing function-level documentation alongside interface and implementation, but if I put the documentation in both the .cpp and the .h file it's duplicated in two places and easily gets out of date.

Re:The C++ Build Process Explained by beached_whale on Wednesday November 14, 2018 @11:30PM (#18456869)
The order of headers mattering is very rare though and not indicitive of normal C++ code. It's rare, but not unheard of unfortunately as most C code is also available to C++.
Re:The C++ Build Process Explained by westmeal on Wednesday November 14, 2018 @09:52PM (#18456497)
Does #pragma once count as an include guard in your book?
Re:The C++ Build Process Explained by jchb on Wednesday November 14, 2018 @10:10PM (#18456578)
#pragma once is not in any C++ standard, although basically all modern compilers support it. The reason it is not in the standard is that its behaviour in the presence of symlinks may differ depending on the filesystem and compiler. Is it the unique combination of filename + content that should be included once or is it the file path? etc.
Re:The C++ Build Process Explained by josefx on Thursday November 15, 2018 @05:33AM (#18458254)
> The reason it is not in the standard is that its behaviour in the presence of symlinks may differ depending on the filesystem and compiler

I have the feeling that the difference matters to almost nobody and should be avoidable by the few people it actually affects. I had more issues with colliding include guards than I had with symlinks and I still end up replacing CLASSNAME_H with PROJECT_CLASSNAME_H in our own headers every now and then since the autogenerated guards are too naive.

Re:The C++ Build Process Explained by tjoff on Thursday November 15, 2018 @02:42AM (#18457577)
> You have to pay attention to include order! You have to write forward declarations! You have to write include guards, in 2018!

Include order is a valid complaint, the others truly are non issues.

I mean, we have other more pressing issues. Such as the fact that garbage collection is still a thing in 2018.

> ...but nowadays a proper documentation generator gives you better...

Examples of that?

> I like seeing function-level documentation alongside interface and implementation, but if I put the documentation in both the .cpp and the .h file it's duplicated in two places and easily gets out of date.

This is the only thing that I dislike about header files. And it is a constant reminder how bad IDEs are at handling this.

Re:The C++ Build Process Explained by omegote on Thursday November 15, 2018 @03:08AM (#18457668)
Doxygen can generate a list of all class members, including those from all ancestors. If you wanna do that checking header files you'll have to keep jumping between files going from parent to parent class.
Re:The C++ Build Process Explained by tjoff on Thursday November 15, 2018 @03:13AM (#18457689)
... doxygen, no. I'd rather take header files than doxygen.

I hope he meant something else since he said "nowadays". Doxygen is ok if you haven't setup your build environment. But for when you actually are in the code (and somewhat familiar with it) I very much prefer just reading the header files than switching to a browser.

Re:The C++ Build Process Explained by enriquto on Thursday November 15, 2018 @04:18AM (#18457953)
> Header files absolutely are a hassle to keep up to date.

This happens because there is some redundancy between the header file and the source. You can often arrange your code so that there is no redundancy (or almost zero).

Re:The C++ Build Process Explained by jcelerier on Thursday November 15, 2018 @05:50AM (#18458318)
> Header files absolutely are a hassle to keep up to date.

how so ? every IDE I've used automatically changes the header if you change the cpp and conversely

Re:The C++ Build Process Explained by tech_tuna on Wednesday November 14, 2018 @07:34PM (#18455740)
>it really isn't any hassle to speak of to keep them up to date

Dear lord, all those broken builds I've seen over the years would love to talk to you about that.

Re:The C++ Build Process Explained by tomcam on Thursday November 15, 2018 @12:36AM (#18457103)
> But still, no IDE or tool I've seen does this better.

Why aren't Delphi, Oberon, and Modula-3 immensely better? Each file is an independent module except the one with main. Then the compiler quickly figures out in one pass what all the function signatures are. The minor drawback with this is that the one-pass method requires you to pay attention to declaration order, though a two-pass method renders that approach irrelevant. The IDE has all the symbolic goodness you need, and no goddamn header files. Also it's still easier than dealing with managing the dependency graph yourself as is necessary with C file include order.

I say this as a guy who enjoys C and has done way more work in C than Delphi, but give credit where it's due.

Re:The C++ Build Process Explained by roel_v on Thursday November 15, 2018 @03:59AM (#18457865)
Because there is no separation between API and implementation? Header files are great, you don't have to wade through implementation details or documentation to figure out what is API and what isn't, even more so with good use of pimpl. You hardly need separate documentation tools, because the header (when structured well) is just the signatures and documentation. Templates muddy this a bit, but you put those in .inl files and it all works conceptually very pure again.

Headers (or at least 'separate specification of API') are/is great, and I miss it dearly in other languages.

Re:The C++ Build Process Explained by pjmlp on Thursday November 15, 2018 @07:28AM (#18458685)
That is what module navigation tools are for.

Already in Turbo Pascal for MS-DOS there was a TPU dump tool, which would generate a "header file" from a TP module.

Re:The C++ Build Process Explained by roel_v on Thursday November 15, 2018 @08:00AM (#18458893)
Sure, there are a bunch of tools for pretty much every language, but I don't want to deal with that, nor switch between browser and editor for it. I know people complain all the time about headers, I was just saying I love the separation, even if it's an historical accident.
Re:The C++ Build Process Explained by malkia on Thursday November 15, 2018 @02:02AM (#18457441)
I started with Apple ][ Basic, but then moved to Turbo Pascal 3.0, 4.0 and 5.0 and enjoyed quite a lot the "Unit" system. But now came the realization that it was not possible for one to do cyclic references - e.g. "Unit A" refers to symbols from "B", and vice versa... Not that it's a good idea, but "C" linkage is okay with this, and you can split things much easier, thus probably ruining things in the long term... but sometimes it can help...

"Uses BGI;" and I'm done :)

Re:The C++ Build Process Explained by pjmlp on Thursday November 15, 2018 @02:36AM (#18457555)
Ah, someone did not read the Turbo Pascal manual! :)

Thankfully there is Bitsavers.

"Circular Unit References" chapter on the Turbo Pascal 5.0 programmer's user guide.

https://archive.org/details/bitsavers_borlandturVersion5.0Us...

Re:The C++ Build Process Explained by malkia on Monday November 19, 2018 @02:32AM (#18484711)
Ha, I must've forgot about it completely! Thank you! And yes, I've never read the manual (unlike the tons of "C" books that followed). Pascal was simply thought orally, and on your own (back then), and that was the case for quite a lot of apps....
Re:The C++ Build Process Explained by tjoff on Thursday November 15, 2018 @02:44AM (#18457584)
I meant that an IDE could provide you with the overview that a header file does.

But they don't.

Also, a header file will hopefully be logically structured by a human. Something generated by the IDE will not.

Re:The C++ Build Process Explained by pjmlp on Thursday November 15, 2018 @07:32AM (#18458713)
The ones for languages with native support for modules do provide such overviews, usually named as something browser.
Re:The C++ Build Process Explained by tjoff on Thursday November 15, 2018 @08:07AM (#18458937)
And all I've seen are quite inferior to the header file. They are a nice addition and can be used for quick navigation. But it does not diminish the usefulness of the header file.
Re:The C++ Build Process Explained by pjmlp on Thursday November 15, 2018 @08:24AM (#18459029)
"Header files" can be generated as well.

You could even make them part of the build, if you want a continuous up to date version.

Re:The C++ Build Process Explained by tjoff on Thursday November 15, 2018 @09:12AM (#18459316)
As I said, a header file will hopefully be logically structured by a human. Something generated by the IDE will not.

That makes a huge difference.

Re:The C++ Build Process Explained by int_19h on Wednesday November 14, 2018 @07:10PM (#18455598)
Separation of module interface and module implementation is useful, but headers are a horrible way to do that. For an example of how it can be done right, look at Borland Pascal dialects (with "interface" and "implementation" section in each unit, where "interface" can be extracted if desired), or Ada, or ML.
Re:The C++ Build Process Explained by DenisM on Wednesday November 14, 2018 @09:49PM (#18456481)
> no IDE or tool I've seen does this better.

Modula-2 does it better. Each module split in two files - definition and implementation. Unlike C++ header files, def files are compiled rather that inlined, so they don't have a potential to produce different result for each source file that references them. The result is 100% reliable dependency graph across all source files, no need for a makefile, and blazingly fast compilation (each def file is only parsed and compiled once).

Re:The C++ Build Process Explained by codesmythe on Wednesday November 14, 2018 @11:37PM (#18456900)
OCaml also does it better. Its split between .ml (implementation) and .mli (definition) files is taken directly from Modula-2 [1].

[1] https://discuss.ocaml.org/t/what-is-the-reason-of-separation...

Re:The C++ Build Process Explained by setpatchaddress on Wednesday November 14, 2018 @07:46PM (#18455830)
That has not been my experience in 35 years of dealing with headers in C and C++ in large-scale software projects. Ruby, Swift, and even Python (or Pascal if we want to go way back) are significantly easier to deal with for anything sophisticated.
Re:The C++ Build Process Explained by kazinator on Wednesday November 14, 2018 @07:52PM (#18455877)
> An IDE should be able to automatically generate something like a header file regardless of language.

https://www.hwaci.com/sw/mkhdr/

Re:The C++ Build Process Explained by hazz99 on Thursday November 15, 2018 @03:52AM (#18457834)
I quite like how Ada has "specification" files, which describes the methods (procedures, entries, functions), types and contracts for everything.
Re:The C++ Build Process Explained by garaetjjte on Wednesday November 14, 2018 @05:23PM (#18454707)
Re:The C++ Build Process Explained by grandmczeb on Wednesday November 14, 2018 @06:04PM (#18455062)
The draft for the merged version expected to be in C++20 is here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p110...
Re:The C++ Build Process Explained by jacobush on Thursday November 15, 2018 @03:14AM (#18457693)
I use https://github.com/mjspncr/lzz3 - it generates header files and source files. If feels quite weird and nice. :)
Re:The C++ Build Process Explained by flukus on Wednesday November 14, 2018 @05:53PM (#18454979)
If java is any guide developers would go out of their way to add interfaces everywhere anyway, often they're essentially header files for the OO gods.
Re:The C++ Build Process Explained by kjeetgill on Wednesday November 14, 2018 @07:18PM (#18455646)
I haven't done any C++ in a while, can you swap out the implementation backing a header?

I suppose at the linker layer maybe but even that is just once globally for the program. Right? The OOP God's demand polymorphism!

If memory serves me, the C++ version is a class with all empty virtual functions.

Re:The C++ Build Process Explained by flukus on Wednesday November 14, 2018 @07:40PM (#18455785)
Yeah, it can only be swapped out at link time (without crazy magic). That satisfies two very common cases though, when there is only one implementation (and the interface is a sacrifice to the OOP gods) and when the second implementation is for unit testing only.

Those 2 plus other crazy rules (like only one interface defined per file) mean that many projects will end up with just as many interface files as a c++ project would header files.