how to write path of external file in an included c++ source - c++

Suppose I'm writing a library or a set of tools mytool where a class MyTool that other people can use is defined. Suppose I have a directory tree like this:
project
| - program1
| - main1.cpp
...
| - mytool
| - mytool.h
| - mytool.cpp
| - data.txt
in tool1.cpp I use the external binary huge file data.dat:
ifsteam f("data.txt");
the main1.cpp use mytool, but if mytool.(s)o is linked with main1.o the program can't find data.dat, for this case I need to change the previous line to:
ifstream f("../mytool/data.txt");
but I can't know where other people put mytool for example they can have a different directory tree:
project
| - program1
| - main1.cpp
| - mytool
| - tool1.h
| - tool2.cpp
| - data.dat
In addition (am I right?) the path depend on where the program is executed.
The only solution I can imagine is to pass to the class contructor MyTool the path of data.dat but I want to keep hidden this file for the user.

You need to know the absolute path of the file, or else the path of the file relative to your working directory. One approach is to have a configuration script which the user runs before compiling your program. The script then hardcodes into your program the relevant path, so the program has the path hardwired in a manner customized for the user.
Sometimes that's not an option because you don't want to distribute the source code, or because you wish to allow the path to change at runtime. Then you can read a configuration file at runtime which says where the file is. But this is just a layer of abstraction: you still need to know where that configuration file is. You might, for example, ask the system where the user's personal directory is, and then find the file there at that directory. This is a sort of mix between compile-time and runtime computation of the path.

One option would be to use an environment variable for the location of your tools. For instance, name it MYTOOLDIR. You can set the path on installation of MyTool. A call to getenv("MYTOOLDIR"); can resolve the path.
On windows, within the mytool dir, run SETX PATH=%PATH%;./, or on Linux, just PATH=$PATH:./. (Provide a set_env.bat or whatnot to do it.)

You'll need to make the location of the binary file a configuration value that the user defines on a particular installation of the program. Or, more easily, just always put the binary file in the same place as the final executable and use "data.dat" as the path.

Related

How to write files to current directory instead of bazel-out

I have the following directory structure:
my_dir
|
--> src
| |
| --> foo.cc
| --> BUILD
|
--> WORKSPACE
|
--> bazel-out/ (symlink)
|
| ...
src/BUILD contains the following code:
cc_binary(
name = "foo",
srcs = ["foo.cc"]
)
The file foo.cc creates a file named bar.txt using the regular way with <fstream> utilities.
However, when I invoke Bazel with bazel run //src:foo the file bar.txt is created and placed in bazel-out/darwin-fastbuild/bin/src/foo.runfiles/foo/bar.txt instead of my_dir/src/bar.txt, where the original source is.
I tried adding an outs field to the foo rule, but Bazel complained that outs is not a recognized attribute for cc_binary.
I also thought of creating a filegroup rule, but there is no deps field where I can declare foo as a dependency for those files.
How can I make sure that the files generated by running the cc_binary rule are placed in my_dir/src/bar.txt instead of bazel-out/...?
Bazel doesn't allow you to modify the state of your workspace, by design.
The short answer is that you don't want the results of the past builds to modify the state of your workspace, hence potentially modifying the results of the future builds. It'll violate reproducibility if running Bazel multiple times on the same workspace results in different outputs.
Given your example: imagine calling bazel run //src:foo which inserts
#define true false
#define false true
at the top of the src/foo.cc. What happens if you call bazel run //src:foo again?
The long answer: https://docs.bazel.build/versions/master/rule-challenges.html#assumption-aim-for-correctness-throughput-ease-of-use-latency
Here's more information on the output directory: https://docs.bazel.build/versions/master/output_directories.html#documentation-of-the-current-bazel-output-directory-layout
There could be a workaround to use genrule. Below is an example that I use genrule to copy a file to the .git folder.
genrule(
name = "precommit",
srcs = glob(["git/**"]),
outs = ["precommit.txt"],
# folder contain this BUILD.bazel file is tool which will be symbol linked, we use cd -P to get to the physical path
cmd = "echo 'setup pre-commit.sh' > $(OUTS) && cd -P tools && ./path/to/your-script.sh",
local = 1, # required
)
If you're passing the name of the output file in when running, you can simply use absolute paths. To make this easier, you can use the realpath utility if you're in linux. If you're on a mac, it is included in brew install coreutils. Then running it looks something like:
bazel run my_app_dir:binary_target -- --output_file=`realpath relative/path/to.output
This has been discussed and explained in a Bazel issue. Recommendation is to use a tool external to Bazel:
As I understand the use-case, this is out-of-scope for building and in the scope of, perhaps, workspace configuration. What I'm sure of is that an external tool would be both easier and safer to write for this purpose, than to introduce such a deep design change to Bazel.
The tool would copy the files from the output tree into the source tree, and update a manifest file (also in the source tree) that lists the path-digest pairs. The sources and the manifest file would all be versioned. A genrule or a sh_test would depend on the file-generating genrules, as well as on this manifest file, and compare the file-generating genrules' outputs' digests (in the output tree) to those in the manifest file, and would fail if there's a mismatch. In that case the user would need to run the external tool, thus update the source tree and the manifest, then rerun the build, which is the same workflow as you described, except you'd run this tool instead of bazel regenerate-autogenerated-sources.

Accessing .in files from a different directory

Suppose that I add a program to path that is dependent on a file name "test.in". I programmed this in C++ so I used ifstream fin("test.in") without specifying the directory. Now if I were to run this program from a different directory, would the program be able to access the file "test.in"?
Firstly, this has nothing to do with the file extension, which is merely a convention given as part of the filename.
Secondly, you were always using a relative path. Even when you were running your program "from the same directory" as test.in, you were reliant on the "working directory" of your shell context being the same as the directory in which the executable and the file reside.
This is not always the case.
For example:
~/myProject:# ls
test.in
program
~/myProject:# ./program
This is okay, because your shell is at ~/myProject, and so is test.in.
However, if you'd written:
~/myProject:# cd ..
~:# ./myProject/program
…then your test.in file wouldn't be found, as it does not exist in ~. It exists in ~/myProject. It doesn't matter that the executable itself is also found in ~/myProject.
This is actually desirable behaviour, as it allows flexibility from the shell. Ideally you would allow support for piping/redirecting the file to the process instead (program < test.in — now there are no assumptions baked into your code at all!), but we can save that for another day.
For now, you seem to be concerned about what happens if you move the executable away. Don't worry: just use this feature!
~:# mv myProject/program .
~:# cd myProject
~/myProject:# ../myProject
Your working directory is the directory in which test.in resides, so it will be found via the relative path given in your program code.

Opening a file in the current directory

I'm trying to load files, and previously I was using hardcoded file locations, (like "c:\location\file.txt") but now that a few friends are also using the file, I'd like to allow them to put the executable wherever they want.
my current code looks like:
ifstream myfile;
myfile.open("c:\\client\\settings.cfg");
I'm trying to change it so that the user puts their executable into whatever folder they want, and then they create a folder and put their settings file into it and the exe will load that with their settings.
ifstream myfile;
myfile.open("\\settings\\settings.cfg");
I have some basic error handling in place, and now the program always errors out saying that it can't find the file.
The file structure looks like this:
[ART]
asset.png
[SETTINGS]
settings.cfg
client.exe
This seems like a really simple thing to do, but I can't find any way to do it. Every example and tutorial about reading and writing to files deals only with files in the executable's directory, or hardcoded into c:\folder...
Could anyone point me to how I do this?
The search path for most systems starts with the current working directory and then to a PATH environment variable. So, all you need to do is specify the file/folder without the absolute path markings and it will use the path relative to the working directory:
ifstream myfile;
myfile.open("settings\\settings.cfg");
// ^^ Note the lack of \\ to start the file path
Paths beginning with \ are always relative to the current drive's root directory. If the current drive is C:, then \settings\settings.cfg means C:\settings\settings.cfg.
Note that you can use / in order to avoid escaping everything. So you can use: settings/settings.cfg. This will be relative to the user's current directory. Note however, that this doesn't necessarily correspond to the directory where the executable resides. If you need the directory of the executable, then you need to use a Windows API function to get it:
#include <Windows.h>
// ...
HMODULE module = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(module, path, MAX_PATH);
Now if you want to open settings/settings.cfg relative to the directory of the executable, create a path that starts with path and append /settings/settings.cfg to it.

Executable using images in different directory

I have the following directory structure
(root)
/ | \
bin resources src
| | |
Debug dot.bmp .cpp
|
.exe
I would like the .exe file to use dot.bmp.
Here's the code from the .cpp file that loads dot.bmp
player_img = il->load_image( "dot.bmp" );
I feel like I need to modify this line of code, but after changing it to:
player_img = il->load_image( "../resources/dot.bmp" );
I still get an error saying that the image couldn't be loaded.
What do I need to change? Is this even possible, or should I just give up and put the image in the same directory as the .exe?
You need to go down one further level in order to get to the root.
../../resources/dot.bmp
Your executable is in bin/Debug but I think you coded under the assumption that it is in bin.
Assuming you are on Windows, the relative path will be relative to the current working directory rather than the directory where the executable resides. Often they are the same thing, but not necessarily.
I would be inclined to use fully-qualified paths and pre-pend the directory where the executable lives. You can obtain this by calling GetModuleFileName passing NULL as the hModule argument. This will return the full path to the executable so you will need to strip off the file name portion.
You will also need to think about deployment. This structure looks like your development structure but you may want a different organisation when you deploy the program. For example, I'd expect the executable to live in the bin directory when deployed.
One final thought. Assuming the images that your program needs is known at compile time it would be much easier to link them into the executable as resources. That way you simply don't have to worry about these issues at all and the executable can stand alone.

c++ not able to find file (i think)

I have the following code (simplified)
int main()
{
ifstream myFile("input.txt");
if(myFile.is_open())
cout<<"test";
}
However myFile.is_open() is returning false;
why?
I'm using eclipse and the input.txt file is right in the src folder with the .cpp file...
Thanks!
Every executable is - by default - ran from the directory of your project. So for a tree like this:
project
|- src
| |- a.cpp
| |- b.cpp
| |- foo.txt
|
|- Debug
| |- a.exe
|
|- foo2.txt
You shall use the paths like src/foo.txt or foo2.txt. Your exe is located in Debug directory, but it will be ran from your project's directory anyway. In this way, every build configuration's executables are ran in the same way (from the same place).
You can change your working directory and arguments under Run configurations (pull-down menu next to the "run" icon). Switch to tab "Arguments", uncheck "use default" and set whichever you'd like if the default's not OK for you.
You have the input.txt in your source folder but the application is executed from the Debug folder; from the 'source', it's located at ..\Debug. Just put your text file there and it should work fine.
If you do not specify a full path, the program will search in the current directory, that probably in your case is the directory where the executable is built edit: it turns out that by default it's the project directory (thanks #Kos).
You can move input.txt in that directory, specify a full path (or a correct relative one) or instruct your IDE to start the executable with a different current directory (I don't know if with Eclipse it's possible, I suggest this because with VS you can do that).
By the way, in general you should avoid to hardcode paths in your sources.
Check the working directory Eclipse sets for you. It might be that of the binary. Try placing the input.txt file in the same directory as that of the binary Eclipse compiles from your code, and then run the application.
To find the working directory setting of your project look in the project's settings dialog.
That said, it's probably a good idea to code in a manner independent from your working directory. You might set the binary's install directory in preprocessor constant string, and have your install script (or configuration script) set that #define to the proper directory. Or, if that's not an option, then you might fetch the directory value at runtime.
You probably have to set up eclipse's working directory to your src directory. I have no idea what the default is.