Compile a program with local file embedded as a string variable? - c++

Question should say it all.
Let's say there's a local file "mydefaultvalues.txt", separated from the main project. In the main project I want to have something like this:
char * defaultvalues = " ... "; // here should be the contents of mydefaultvalues.txt
And let the compiler swap " ... " with the actual contents of mydefaultvalues.txt. Can this be done? Is there like a compiler directive or something?

Not exactly, but you could do something like this:
defaults.h:
#define DEFAULT_VALUES "something something something"
code.c:
#include "defaults.h"
char *defaultvalues = DEFAULT_VALUES;
Where defaults.h could be generated, or otherwise created however you were planning to do it. The pre-processor can only do so much. Making your files in a form that it will understand will make things much easier.

The trick I did, on Linux, was to have in the Makefile this line:
defaultvalues.h: defaultvalues.txt
xxd -i defaultvalues.txt > defaultvalues.h
Then you could include:
#include "defaultvalues.h"
There is defined both unsigned char defaultvalues_txt[]; with the contents of the file, and unsigned int defaultvalues_txt_len; with the size of the file.
Note that defaultvalues_txt is not null-terminated, thus, not considered a C string. But since you also have the size, this should not be a problem.
EDIT:
A small variation would allow me to have a null-terminated string:
echo "char defaultvalues[] = { " `xxd -i < defaultvalues.txt` ", 0x00 };" > defaultvalues.h
Obviously will not work very well if the null character is present inside the file defaultvalues.txt, but that won't happen if it is plain text.

One way to achieve compile-time trickery like this is to write a simple script in some interpreted programming language(e.g. Python, Ruby or Perl will do great) which does a simple search and replace. Then just run the script before compiling.
Define your own #pramga XYZ directive which the script looks for and replaces it with the code that declares the variable with file contents in a string.
char * defaultvalues = ...
where ... contains the text string read from the given text file. Be sure to compensate for line length, new lines, string formatting characters and other special characters.
Edit: lvella beat me to it with far superior approach - embrace the tools your environment supplies you. In this case a tool which does string search and replace and feed a file to it.

Late answer I know but I don't think any of the current answers address what the OP is trying to accomplish although zxcdw came really close.
All any 7 year old has to do is load your program into a hex editor and hit CTRL-S. If the text is in your executable code (or vicinity) or application resource they can find it and edit it.
If you want to prevent the general public from changing a resource or static data just encrypt it, stuff it in a resource then decrypt it at runtime. Try DES for something small to start with.

Related

Config File using Environment Variables

I have a .cfg file and I'd like to use an environment variable to configure one of the fields.
directory=${HOME}/folder1/
However, when I parse this config, it's reading ${HOME} as a string, which is obviously not what I want.
I wrote my own parser in C++, in case I need to do something special. Right now it is a very basic read and parse.
void Config_Parser::parse_config_by_delimiter(string config, string delimiter) {
ifstream infile(config);
while (infile >> line) {
key = line.substr(0, line.find(delimiter));
value = line.substr(line.find(delimiter)+1);
if (this->config_settings.find(key) != this->config_settings.end()) {
cout << "Cannot use config... same key is set multiple times" << endl;
}
this->config_settings.insert({key, value});
}
}
The code seems to work fine for all other config settings (anything not using an environment variable), so I don't think its a problem with the code. But, I am a C++ noobie, so it's here anyways.
When I parse and print out the value:
Actual output: ${HOME}/folder1/
Expected/desired output: /home/my_dir/folder1/
Untested
You can use wordexp to do posix shell-like expansion of strings.
The function wordexp() performs a shell-like expansion of the string
s and returns the result in the structure pointed to by p.
You will need to #include <wordexp.h>
You also probably want to specify the flag WRDE_NOCMD to prevent subshell command execution.
http://man7.org/linux/man-pages/man3/wordexp.3.html
Is the following configuration syntax acceptable to you?
directory = getenv("HOME") + "/folder1/";
If so, then a configuration file parser library I wrote called Config4* can do what you want. You can find it on http://www.config4star.org.
I recommend you scroll down the web page to "Download the Manuals" and retrieve Config4* Getting Started Guide and Config4* C++ API Guide. Chapters 2 (overview of syntax) and 3 (overview of API) of the "Getting Started" guide should be more than sufficient to get you up and running.

Variables in quotation marks C++

Let me begin by saying that I could not find an identical question, but my search keywords were fairly generic, and so if you know of a thread that answers my question, point it out to me and I'll close this thread.
I'm re-writing a bash script of mine in c++ to help me get a firmer grasp on the language. The problem I'm running into is as follows:
string input = "/wam/wxx.cpp";
string output = "/wam/wxx.exe";
system ("/MinGW/bin/g++.exe input -o output");
(This is just a small illustration; in my actual code the variables are user-input)
Obviously I am passing the words 'input' and 'output' to my compiler instead of the variables of those names. I have tried
system ("/MinGW/bin/g++.exe" input "-o" output);
as well as other combinations of quoting/not quoting, none of which work either. The system command wants quotation marks, so is there a way to have my variables properly recognized in those quotes? (Currently I am saving these variables to a text file, then loading them into a bash script that runs the compiler, which defeats the purpose of me writing this in c++ to begin with.)
Thanks in advance!
EDIT: To clarify, I am
using namespace std
Since they are (presumably) std::string, you can just paste them together with +, like this:
std::string cmd = "/MinGW/bin/g++.exe";
std::string fullcmd = cmd + " " + input + " -o " + output;
system(fullcmd.c_str());
You need fullcmd.c_str() since system takes a C style string, not a C++ style string.
This should help:
string input = "/wam/wxx.cpp";
string output = "/wam/wxx.exe";
string command = "/MingW/bin/g++.exe "
command += input;
command += " -o "
command += output;
system(command.c_str());
You need std::string::c_str() to convert strings to char arrays, and you can't add an std::string to a string literal, however, you can add a literal to a std::string, or an std::string to an std::string.

Efficient and stable YAML parser for cocos2d-x

I am developing a game using cocos2d-x and C++, and I need to load a bunch of YAML files for this application. I tried using the yaml-cpp library with quite good results.
The problem is that this library seems to be very unstable (at least under cocos2d-x on iOS), since almost 20% of the time it fails loading the same YAML file throwing "end of map not found", "invalid map element", or errors like these ones.
I followed the HowToParseADocument guide, so I think I got it correct. But, since it's not 100% reliable, I am looking for something more stable. Eg:
long size = 0;
unsigned char *yaml = FileUtils::getInstance()->getFileData("file.yml", "r", &size);
std::stringstream is;
is << yaml;
YAML::Parser parser(is);
YAML::Node doc;
while(parser.GetNextDocument(doc)) {
instance->settings = doc.Clone();
}
The parser usally breaks at the parser.GetNextDocument(doc) call. The document I am trying to read is plain YAML with key: value lists in this simple form:
# Comment
section1:
param1: value1
param2: value2
# Comment
section2:
param1: value1
param2: value2
Edit
I am not allowed to disclose the content of the original YAML file, but I can give you some information:
It only contains maps, and not arrays, aliases or other particular constructs
Those values are integers, float or strings
It has been linted with this free tool, with success.
The code I used to read it, posted up there, it's always in that form, and I do not modify it to make the app run correctly. It's just that the app starts and works or starts and does not work. Since I am changing nothing in the middle, I really do not understand what's happening.
It's a bit hard to guess at the solution because you won't provide an actual example, but:
Who owns the data at the unsigned char* returned by getFileData? If that function itself owns the data, then it is no longer valid after the function returns, and so all sorts of crazy stuff might happen.
To validate what's happening here (beyond looking at the implementation of getFileData), you could print out is.string() before calling YAML::Parser parser(is); and see if that prints the expected YAML.

formatted string to stream

I'm sure I'm not the first one to encounter this situation, still, couldn't find anything specific neither here, nor generally.
I'm using an old logger, that uses the printf formatting... e.g.:
LOG_ERROR("Error Code: %d. Additional data %s", error.ID, error.serialize());
And want to move to a stream-style logger. e.g.:
LOG_ERROR("Error Code: " << error.ID << "Additional data: " << error.serialize());
Naturally, the code base is big and I want to automate this. I think I can come up with a script that does it, but it looks not so trivial... I'm sure there's a pretty simple way.
Thanks!
I would not dare to automate that task.
You may rename all old LOG_ERROR to (something like) LOG_ERROR_FORMAT, let LOG_ERROR_FORMAT create a string and pass that to the new LOG_ERROR.
If you really want to go for it, I suggest a look at some printf implementation to get the parsing right

How to print subscripts/superscripts on a CLI?

I'm writing a piece of code which deals with math variables and indices, and I'd need to print subscripts and superscripts on a CLI, is there a (possibly cross-platform) way to do that? I'm working in vanilla C++.
Note: I'd like this to be cross-platform, but since from the first answers this doesn't seem to be possible I'm working under MacOS and Ubuntu Linux (so bash).
Thank you
Since most CLIs are really only terminals (pretty dumb ones mostly but sometimes with color), the only cross-platform way I've ever done this is by allocating muliple physical lines per virtual line, such as:
2
f(x) = x + log x
2
It's not ideal but it's probably the best you're going to get without a GUI.
Following you extra information as to what platforms you're mainly interested in:
With Ubuntu at least, gnome-terminal runs in UTF-8 mode by default so the following code shows how to generate the superscripts and subscripts:
#include <stdio.h>
static char *super[] = {"\xe2\x81\xb0", "\xc2\xb9", "\xc2\xb2",
"\xc2\xb3", "\xe2\x81\xb4", "\xe2\x81\xb5", "\xe2\x81\xb6",
"\xe2\x81\xb7", "\xe2\x81\xb8", "\xe2\x81\xb9"};
static char *sub[] = {"\xe2\x82\x80", "\xe2\x82\x81", "\xe2\x82\x82",
"\xe2\x82\x83", "\xe2\x82\x84", "\xe2\x82\x85", "\xe2\x82\x86",
"\xe2\x82\x87", "\xe2\x82\x88", "\xe2\x82\x89"};
int main(void) {
int i;
printf ("f(x) = x%s + log%sx\n",super[2],sub[2]);
for (i = 0; i < 10; i++) {
printf ("x%s x%s ", super[i], sub[i]);
}
printf ("y%s%s%s z%s%s\n", super[9], super[9], super[9], sub[7], sub[5]);
return 0;
}
The super and sub char* arrays are the UTF-8 encodings for the Unicode code points for numeric superscripts and subscripts (see here). The given program will output my formula from above (on one line instead of three), then another test line for all the choices and a y-super-999 and z-sub-75 so you can see what they look like.
MacOS doesn't appear to use gnome-terminal as a terminal program but references here and here seem to indicate the standard terminal understands UTF-8 (or you could download and install gnome-terminal as a last resort).
I'd need to print subscripts and superscripts on a CLI, is there a cross-platform way to do that?
Only if you have a Unicode-capable terminal, which is far from guaranteed. Unicode defines a limited number of sub- and superscript ‘compatibility characters’, you certainly can't use it on any old letter:
₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓ
⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿⁱ
Even then you're reliant on there being a glyph for it in the console font, which is also far from guaranteed. Superscript 2 and 3 are likely to exist as they're present in ISO-8859-1; the others may well not work.