0

Is it possible to have a function like this:

const char* load(const char* filename_){
    return
    #include filename_
    ;
};

so you wouldn't have to hardcode the #include file?

Maybe with a some macro?

I'm drawing a blank, guys. I can't tell if it's flat out not possible or if it just has a weird solution.

EDIT:

Also, the ideal is to have this as a compile time operation, otherwise I know there's more standard ways to read a file. Hence thinking about #include in the first place.

  • Use `std::ifstream`. `#include` happens at preprocessing time (roughly at compile time); it can't happen at runtime – Justin Mar 28 '18 at 20:47
  • I don't even understand how you intend to use such a function... replace all of your `#include` directives with `load()`? If you want to read the file as text that is very different than `#include`ing it – Cory Kramer Mar 28 '18 at 20:49
  • You can use `xxd -i` to convert a file to a format suitable for use as data by a C or C++ compiler. – Eljay Mar 28 '18 at 20:51
  • @CoryKramer The purpose would be able to get a file's contents as a string at compile time. OK yep a function like this wouldn't work as I've written it. I'll just have to use const char* = #include"filename" where filename is a usable string and harcode the path as I need it. – Holden Perkins Mar 28 '18 at 20:56
  • 1
    Now I'm a bit confused. What exactly do you want? You just want to include any file (without compilation) and assign it to a string, am I right? Then I think we misunderstood your question. If that is the case, please reword your question a bit to make it clearer. – user2328447 Mar 28 '18 at 21:00
  • Yes, that would be the implication of a function returning a string with the return statement including #include. ( if such a thing were possible as I proposed it, which I now know isn't) – Holden Perkins Mar 28 '18 at 21:03
  • Ah, OK... then your question is a duplicate and has already sufficient answers here: [“#include” a text file in a C program as a char](https://stackoverflow.com/q/410980/2328447) – user2328447 Mar 28 '18 at 21:07

4 Answers4

1

This is absolutely impossible.

The reason is - as Justin already said in a comment - that #include is evaluated at compile time.

To include files during run time would require a complete compiler "on board" of the program. A lot of script languages support things like that, but C++ is a compiled language and works different: Compile and run time are strictly separated.

user2328447
  • 1,579
  • 1
  • 18
  • 24
1

I can't tell if it's flat out not possible

I can. It's flat out not possible.

Contents of the filename_ string are not determined until runtime - the content is unknown when the pre processor is run. Pre-processor macros are processed before compilation (or as first step of compilation depending on your perspective).

When the choice of the filename is determined at runtime, the file must also be read at runtime (for example using a fstream).

Also, the ideal is to have this as a compile time operation

The latest time you can affect the choice of included file is when the preprocessor runs. What you can use to affect the file is a pre-processor macro:

#define filename_ "path/to/file"

// ...
return
#include filename_
;
eerorika
  • 181,943
  • 10
  • 144
  • 256
1

You cannot use #include to do what you want to do.

The C++ way of implementing such a function is:

  1. Find out the size of the file.
  2. Allocate memory for the contents of the file.
  3. Read the contents of the file into the allocated memory.
  4. Return the contents of the file to the calling function.

It will better to change the return type to std::string to ease the burden of dealing with dynamically allocated memory.

std::string load(const char* filename)
{
   std::string contents;

   // Open the file
   std::ifstream in(filename);

   // If there is a problem in opening the file, deal with it.
   if ( !in )
   {
      // Problem. Figure out what to do with it.
   }

   // Move to the end of the file.
   in.seekg(0, std::ifstream::end);

   auto size = in.tellg();

   // Allocate memory for the contents.
   // Add an additional character for the terminating null character.
   contents.resize(size+1);

   // Rewind the file.
   in.seekg(0);

   // Read the contents
   auto n = in.read(contents.data(), size);
   if ( n != size )
   {
      // Problem. Figure out what to do with it.
   }

   contents[size] = '\0';
   return contents;
};

PS

Using a terminating null character in the returned object is necessary only if you need to treat the contents of the returned object as a null terminated string for some reason. Otherwise, it maybe omitted.

R Sahu
  • 196,807
  • 13
  • 136
  • 247
  • 1
    `std::string` is not a null-terminated string, so there is no need to (nor should you) allocate room for a null terminator. And [there are ways](https://stackoverflow.com/questions/116038/) to read a whole file into a `std::string` without resorting to seeking the file. – Remy Lebeau Mar 28 '18 at 21:12
1

it is theoretically possible.

In practice, you're asking to write a PHP construct using C++. It can be done, as too many things can, but you need some awkward prerequisites.

  • a compiler has to be linked into your executable. Because the operation you call "hardcoding" is essential for the code to be executed.

  • a (probably very fussy) linker again into your executable, to merge the new code and resolve any function calls etc. in both directions.

Also, the newly imported code would not be reachable by the rest of the program which was not written (and certainly not compiled!) with that information in mind. So you would need an entry point and a means of exchanging information. Then in this block of information you could even put pointers to code to be called.

Not all architectures and OSes will support this, because "data" and "code" are two concerns best left separate. Code is potentially harmful; think of it as nitric acid. External data is fluid and slippery, like glycerine. And handling nitroglycerine is, as I said, possible. Practical and safe are something completely different.

Once the prerequisites were met, you would have two or three nice extra functions and could write:

void *load(const char* filename, void *data) {
    // some "don't load twice" functionality is probably needed
    void *code = compile_source(filename);
    if (NULL == code) {
        // a get_last_compiler_error() would be useful
        return NULL;
    }
    if (EXIT_SUCCESS != invoke_code(code, data)) {
        // a get_last_runtime_error() would also be useful
        release_code(code);
        return NULL;
    }
    // it is now the caller's responsibility to release the code.
    return code;
}

And of course it would be a security nightmare, with source code left lying around and being imported into a running application.

Maintaining the code would be a different, but equally scary nightmare, because you'd be needing two toolchains - one to build the executable, one embedded inside said executable - and they wouldn't necessarily be automatically compatible. You'd be crying loud for all the bugs of the realm to come and rejoice.

What problem would be solved?

Implementing require_once in C++ might be fun, but you thought it could answer a problem you have. Which is it exactly? Maybe it can be solved in a more C++ish way.

A better alternative, considering also performances etc., to compile a loadable module beforehand, and load it at runtime.

If you need to perform small tunings to the executable, place parameters into an external configuration file and provide a mechanism to reload it. Once the modules conform to a fixed specification, you can even provide "plugins" that weren't available when the executable was first developed.

Community
  • 1
  • 1
LSerni
  • 49,775
  • 9
  • 56
  • 97