12

There was a similar question here but it had no valuable information so I want to ask this again - is it possible to load contents of an arbitrary file using a constexpr function? I know that this seems impossible since all the possible functions that allow file I/O (fopen, open ...) are not constexpr so cannot be called in this scenario. However - since there are many people here who follow the development of c++17 and on - is there any hope that further standards will include some file I/O API that will be constexpr and could be used to load a file at compile time?

Just for a comparison - Haxe allows to do almost anything via the compile time macros, so it would be very cool to have something similar in C++. For example, generating class instances by deserializing files.

Community
  • 1
  • 1
Rudolfs Bundulis
  • 11,636
  • 6
  • 33
  • 71
  • 4
    Sounds like what you need could be done with a small tool that loads a file (at runtime) and generates some .h/.cpp which you then `#include` into your code. – Anedar Mar 02 '16 at 09:59
  • 1
    Generating class instances is done by template instantiation. Those tempaltes can come from #include'd headers. So you don't even need a tool to generate those headers. Template instantiation is Turing complete. – MSalters Mar 02 '16 at 10:04
  • @Anedar of course, I have that that numerous times. But consider other uses cases. I totally agree that everything can be done via external tools, simply this does not seem that hard to add like reflection etc. It merely needs the compiler to be able to understand that it needs to evaluate the `constexpr` functions ahead of time and then put the result back in the translation unit. – Rudolfs Bundulis Mar 02 '16 at 10:39
  • 1
    This is already solved by things like [m4](http://www.gnu.org/software/m4/m4.html) and build scripts/tools as it makes more sense for this to be a part of a *pre*-process. – RamblingMad Mar 02 '16 at 10:50
  • 1
    @CoffeeandCode again, agreed:) The aim of the question was to gain knowledge whether there are plans to do this in C++17 or further on. I am not trying to compare or prove superiority of either approach. – Rudolfs Bundulis Mar 02 '16 at 11:02
  • @CoffeeandCode =high five= – John Bargman Mar 02 '16 at 13:05
  • this have to be something similar to #include to be implemented – Sergei Krivonos Aug 29 '18 at 12:02

2 Answers2

9

This is (to me at least) a typical case of over-complication when moving between languages. Yes, you could implement functionality to add compile-time file loading into C++, we could also add runtime-reflection. However in my opinion these aren't necessary, nor wanted features.

If you have the skill to write a program that needs a constexpr, you can comprehend the concept of pre-build events (to use the Microsoft terminology), should you be capable of reading a file, you should be sufficiently capable of writing code to parse files into other formats.

In this situation, the compile-time solution is to write a stand-alone tool that will parse the file in question, and generate a .h (header) file containing the required constants and evaluations of those constants. This tool can then be run before your build-process is run.

Thus your constexpr becomes exactly that, a constant expression

Take the example of a "program version number", the common solution is to have a developers IDE, or stand-alone tool, create a header-file containing the constant values that are the version of the software.

Many FOSS software's contain such functionality in their ./configure scripts - parsing the current revision number from Git or alike - thus meaning that any built-version of the software can output the build-number, allowing issue tracking.

In short - I would be very surprised and disappointed if c++17 or any other future version presented the functionality you are looking for - doing so adds a requirement to understand the file-system of the user, and only succeeds in implementing functionality best left to the end user.

However, we should not fall into the trap of trying to add every possible feature to it in an attempt to please everyone.~ Bjarn Stroustrup

In layman's terms, it would be like selling a push-bike along with a single-size helmet and biking shorts (that the user is legally required to use). It might fit the needs of most users, but attempting to fit into said shorts with my waistline would be... problematic.

Imagine the complexities added for cross compilers between architectures, embedded systems, etc.

[edit/]

This isn't perhaps a perfect answer - and is to some degree opinion based; What I'm attempting to provide is an understanding that while arguably most users are desktop-oriented, adding something like this would immensely complicate compiler development for embedded systems and other applications. And this is what C++ is trying to avoid.

Some light reading

[edit 2/]

It was indicated in the comments "why not just have the compiler just add the file-contents as a char *, and that is a great question! In this regard I have two points to raise.

  1. Are you going to parse that char* in the constantexp? If so, then you are going to need a loop, and variables - things that you can't use reliably.
  2. If you want a string literal of the file contents - it's not pretty but C++'s preprocessor can do this (assuming the file-format doesn't contain invalid characters).

And again - anything more complex than these simple examples becomes complex enough to be hard to define in the standard, should the file be binary? should it be text? What are the line-endings? should it be parsed?

Meanwhile, you could easily Use existing tools, even doing so in combination with other methods. For example, write a tool that will parse your binary file, re-encode it as a series of escape sequences (eg: \0x0, \0x12, \0x22), have it automatically added to a header file defining a pointer to said data. Then #include that header - You have what you want, for the platforms your working on, and haven't possibly created loopholes in the C++ standard that may cause inherent complexity.

Please remember that C++ as a language is finally recovering from a very-long spate of Very Bad Press. And Compilers that to this day Still act totally-differently in some instances.

In my opinion, Every C++ programmer wants a world where I can write my "perfectly object oriented C++ code once" and then it will just work on everything from a 1990's Nokia-phone, through to the latest quantum-optical-univac. And finally we are getting a little bit closer to doing so, to proving the worth of C++ to the wider world. Firing our death-star beam at Java, or somesuch provocative statement.

Thus, I say - The idea is nice, but not-needed. Of course, if you'd like to write a Full Proposal, outlining how you will avoid any issues that could hurt the future C++-Utopia, I would beg you to do so. If a feature like this can be done without hurting the pre-processor, platform independence, creating Impossibly complex errors, or alike. Then you'd be smarter than me - Because I can't see how that would be possible currently.

Community
  • 1
  • 1
John Bargman
  • 1,267
  • 9
  • 19
  • Well, I understand your point - but then again the compiler actually does not have to understand the file system. All I want is a `constexpr` function that for example gives me a `char*` with the file contents. I mean it can be the same `fopen`. I understand that it is harder for compiler to understand that a given function requires to be compiled ahead of the whole translation unit, evaluated and then the result put back inside the code, but still, it does not have to know anything about the file system - leave that to `fopen`. – Rudolfs Bundulis Mar 02 '16 at 10:36
  • And when parsing that char*, will you use a loop? Will that loop definitely-end? Will you need variables to handle that pointer? I will extend my answer. – John Bargman Mar 02 '16 at 11:13
  • @RudolfsBundulis, take a look at my update - I hope it helps clarify my point, – John Bargman Mar 02 '16 at 11:40
  • I got your point from the first version of the response and I don't have anything against it:) I already said, that I'm not trying to imply that the existing means of doing this are dis-functional. The intent of the question was more of "if we have `constexpr` why isn't there a set of `constexpr` functions that would allow to do trivial and simple tasks". Maybe you misunderstood me (judging from the note about loops) - I don't want the compiler to be able to run fully functional C++ code during compilation, but a tiny subset would be nice. – Rudolfs Bundulis Mar 02 '16 at 13:10
  • @RudolfsBundulis, ah my apologies, it would appear I did indeed misunderstand. – John Bargman Mar 02 '16 at 13:14
  • That is why I tried to simply inquire "is there anything like that in the making":) I did not intend to say "now march and make me the ultimate compiler" :D – Rudolfs Bundulis Mar 02 '16 at 13:25
  • @RudolfsBundulis, well I couldn't just answer "no" - I like words too much :) – John Bargman Mar 04 '16 at 13:23
  • @JohnBargman I thought that it could be intereseting to *load* data from file during compilation. My motivation is to make it easier to embed static resources which fits well on embedded systems [expensive computation of LUT can be done beforehand], as well as on desktop systems [UI icons etc]. Then you can have your data in a constexpr array, and do not need to worry about external pointers. You also gain type safety. Otherwise, you need raw byte pointers, that are casted to the correct type. – user877329 Oct 20 '16 at 08:26
1

With C23 the #embed directive was introduced. You can read more here.

#embed allows you to directy embed a file directly into your file. Very useful for all sorts of things.

An example from the link is:

#include <assert.h>

int main (int, char*[]) {
    static const char sound_signature[] = {
#embed <sdk/jump.wav>
    };
    static_assert((sizeof(sound_signature) / sizeof(*sound_signature)) >= 4,
        "There should be at least 4 elements in this array.");

    // verify PCM WAV resource signature (at run-time)
    assert(sound_signature[0] == 'R');
    assert(sound_signature[1] == 'I');
    assert(sound_signature[2] == 'F');
    assert(sound_signature[3] == 'F');

    return 0;
}

However, this functionality does not yet exist in the C++ standard (though you may be lucky enough that your compiler will just sort of ignore that).

One way of going around that would be to write a C file, which #embeds your data as a static array or similar, compile that with a compliant C compiler, and then link to it from your C++ file.

Frederik Juul
  • 181
  • 1
  • 11