4

I want to exclude some headers from my include chain after having used them. From what I know there is no exclude "header.h" in c++11.

Pseudo Code Wishful thinking:

#include "the_bad_header.h" //long includechain with later unused declarations
class bulky { ... };
constexpr std::size_t bulkysize = sizeof(bulky);
forget everything included and class bulky and remember only bulkysize

My example where the problem becomes evident follows. Please don't argue this is not a serious problem. The Example is broken down to show the minimal abstract language usage. I will describe the old fashioned solutions and their disadvantages too.

Old style solution

justanotherheader.h:

class bulkywrap
{
public:
    bulkywrap();
protected:
    friend class bulkywrap_pImpl;
    bulkywrap_pImpl *const pImpl; //opaque pointer, private implementation
};

justanothercppunit.cpp:

#include "justanotherheader.h"
#include "boost/lotsofheaders.hpp"
//#include more and more headers of highly complex libraries so adding millions of known types and other identifiers, macros, and so on

class bulkywrap_pImpl
{
    //lots of members of types used from the other libraries
};

bulkywrap::bulkywrap()
: pImpl( new bulkywrap_pImpl() )
{}

My current Solution

justanotherheader.h:

#include "stdint.h" // this is the only header I like to use, but also unnecessary.
#define UNKNOWNSIZE 12345
class bulkywrap
{
public:
    bulkywrap();
protected:
    friend class bulkywrap_pImpl;
    bulkywrap_pImpl *const pImpl; //opaque pointer, private implementation
    uint8_t pImpl_Placement[UNKNOWNSIZE]; //placement new for pImpl
};

justanothercppunit.cpp:

#include "justanotherheader.h"
#include "boost/lotsofheaders.hpp"
//#include more and more headers of highly complex libraries so adding millions of known types and other identifiers, macros, and so on

class bulkywrap_pImpl
{
    //lots of members of types used from the other libraries
};

bulkywrap::bulkywrap()
: pImpl( new(this->pImpl_Placement) bulkywrap_pImpl() ) //using this here is safe
{}

So, the code above is working. The advantages are: hiding complexity and having no runtime dynamic memory indirections. Huh? I mean, the placement new allows the whole object to be put on stack and all member addresses are known at compile time. My attempt is to have best performance while using interface design of opaque pointer.

If you think: "this performance advantage is not worth the thinking effort." then please leave that question.


My expected Solution

justanotherheader.h:

#include "stdint.h" // this is the only header I like to use, but also unnecessary.

constexpr std::size_t get_bulkywrap_pImpl_Size( void ); //constexpr function forward declaration
extern constexpr std::size_t bulkywrap_pImpl_Size; //constexpr literal forward declaration with external initialization

class bulkywrap
{
public:
    bulkywrap();
protected:
    friend class bulkywrap_pImpl;
    bulkywrap_pImpl *const pImpl; //opaque pointer, private implementation
    uint8_t pImpl_Placement[get_bulkywrap_pImpl_Size()]; //undefined constexpr used
    uint8_t pImpl_Placement[bulkywrap_pImpl_Size]; //alternative to above. undefined constexpr used
};

justanothercppunit.cpp:

#include "justanotherheader.h"
#include "boost/lotsofheaders.hpp"
//#include more and more headers of highly complex libraries so adding millions of known types and other identifiers, macros, and so on

class bulkywrap_pImpl
{
    //lots of members of types used from the other libraries
};

constexpr std::size_t get_bulkywrap_pImpl_Size( void )
{
    return sizeof(bulkywrap_pImpl);
}
constexpr std::size_t bulkywrap_pImpl_Size = sizeof(bulkywrap_pImpl);

bulkywrap::bulkywrap()
: pImpl( new(this->pImpl_Placement) bulkywrap_pImpl() ) //using this here is safe
{}

In my current solution I need to verify the sizeof(bulkywrap_pImpl) and adjusting UNKNOWNSIZE manually. I think it is currently not possible to get any information from a compilationunit to others. I know this is typically intended by good reason, but this limits the possibilities of c++11.

I want to point out:

jtc1 sc22 wg21 paper n3337

jtc1 sc22 wg21 paper n3308

Please help me to find information weather and why the standard does not allow this.

But furthermore I would like to find a solution of how to export some literal constant during compiliation time out of a compileunit into another compileunit. It's just a literal, so all statements and expressions are not affected by it. Thus compilation does not depend where the size of the array comes from.

My suggestion results in some work for the ISO-jtc1-sc22-wg21 and the compiler developers, but I don't see any relevant difference between template and constexpr since every definition must appear in the same translationunit. This makes modular programming and clean interfaces bogus.

And no: I don't want to use preprocessor macros, dynamic new or virtual member functions. Of importance is maximal const-correctness, since the size of the class is const.

Please help

balki
  • 26,394
  • 30
  • 105
  • 151
  • You cannot do that in C++. You need to run extra tools as a part of your build process. There's nothing to be afraid of, the world (or a sizable part thereof) is built out of `configure` scripts which do something very similar to what you need: build a program, run it, use its output to build a source of another program. – n. m. could be an AI Feb 19 '13 at 17:55
  • Of course, your solution is reasonable effective. I think I need to use a `configure` approach. Therefore, do I have to write a main function that knows the hidden class and outputs the interface_configure.h? Is there any library available which takes the class-header and provides the file-write in a standard-like way? Sure writing the main and the header-file in c++ is not that difficult, but I think this can be improved automatically and combined with cmake and or make. What are the keywords of these development strategies. – JustAnotherCCppXXDummy Feb 19 '13 at 18:31
  • Or please point out a library from which I can learn that but it should not use it in a too complex style. – JustAnotherCCppXXDummy Feb 19 '13 at 18:35
  • Maybe you trimmed it out for questions-sake, but your current solution doesn't seem to handle alignment. You need that as well as size. – GManNickG Feb 19 '13 at 18:41
  • Sometimes I have seen projects that create extra targets from cmake/make. These extra targets run some code, I guess the configure and stuff. The main target depends on these configure-targets, so they have to be build first. But now I don't know how to tell cmake the configure-target must be run before build main-target. cmake help appreciated. – JustAnotherCCppXXDummy Feb 19 '13 at 18:41
  • since C++11 we have alignas specifier. – JustAnotherCCppXXDummy Feb 19 '13 at 19:07
  • If you can't solve your problem, you might want to put some static asserts that your buffer size matches the size of your `pImpl` class. That would at least reduce the chance you invoke undefined behavior. Second, why the `bulkywrap_pImpl *const pImpl` variable? Why not `bulkywrap_pImpl *const pImpl()`? The only ones who can call it are those who have the `bulkywrap_pImpl` instance visible anyhow. – Yakk - Adam Nevraumont Feb 19 '13 at 20:20
  • @Yakk : Yes I use `static_assert` already and it was removed from my example for clarity reason. Nevertheless `static_assert` can only tell that the size is not correct but it can not output that size automatically to a compiler output or a file. Until now the `sizeof(bulkywrap_pImpl)` must me asigned to a variable and checked during runtime. What do you mean by `bulkywrap_pImpl *const pImpl()` ? – JustAnotherCCppXXDummy Feb 20 '13 at 11:12

2 Answers2

2

you can't have both "compile-time" and "from another compilation unit" at the same time. also it's not clear why do you need to forget successfully parsed header. parsing time already consumed. i suggest you to create another header with size constant, include it from both files, and add static_assert to pimpl file, checking that constant >= sizeof(pimpl). you can generate this header as part of your build system by compiling source file including pimpl and doing cout <<sizeof(pimpl). also i suggest you to not waste time and space for pimpl pointer and replace it with member function, returning address of properly cast buffer. also you failed to show where you call pimpl's destructor. also implementing assignment/copy/move/swap will be a lot of fun

pal
  • 618
  • 4
  • 9
  • yes, your absolutely right. Getting the least effort solution would not make me programming in C++ all the time. My question regards performance. And the most efficient performance solution involves often lots of efforts rewriting "best practice" solutions. destructor calling is pretty easy: this->pImpl->~bulkywrap_pImpl(); assignment/copy/move work the same. – JustAnotherCCppXXDummy Nov 06 '13 at 12:54
  • maybe you should try to switch to using import keyword – Sergei Krivonos Aug 10 '18 at 09:59
0

use static_assert in cpp to check that size declared in header >= size of impl class

Sergei Krivonos
  • 4,217
  • 3
  • 39
  • 54