News for hackers. Stuff that matters.
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++ :)