Why does the starting directory matter when launching a OpenGL executable? - c++

I am doing a openGL porject and readering my shaders from .vert and .frag glsl files. I am using CMake with an extension for auto ninja file generation as well. This is my readfile code:
std::string readFile(const char *filePath) {
std::string content;
std::ifstream fileStream(filePath, std::ios::in);
if(!fileStream.is_open()) {
std::cerr << "Could not read file " << filePath << ". File does not exist." << std::endl;
return "";
}
std::string line = "";
while(!fileStream.eof()) {
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
I spent a long time thinking I was crazy because from my build dir I was doing ./app/NAME_OF_APP.exe but the shaders were not being applied just black. It was only when I clicked on it from within the file explorer that it worked so I cd'ed into build/app/ and called ./NAME_OF_APP.exe and boom it worked. Why is this behavior happening? does the starting dir effect the relative pathing of the actual application? seems odd to me. thanks.

If you run a program from a command line the current directory is whatever directory is current in that command prompt. If you run a program from explorer the current directory is the directory that houses the executable.
You could use a function like GetModuleFileName (with NULL as the hModule parameter) to get the path to the executable and then chop off at the last \ and then change to that directory and then the program would not depend on the directory the program is run from.

Related

Unable to open file without it's full path in C++

I want to get file's content and print it in console, but the code bellow works only if I use full path and I don't want that, I am using Visual Studio 2022
int main() {
std::ifstream file("folder/file.txt");
std::string content = "";
if (file.is_open()) {
while (file) {
std::string line;
std::getline(file, line);
content += '\n' + line + '\n';
}
}
else {
std::cout << "Failed to open file" << std::endl;
}
std::cout << content << std::endl;
return 0;
}
output: Failed to open file
but if I use full path like: "C:\Users\User\...\folder\file.txt" it will work.
Some of the shorter comments give clues to the problem, but I'll provide a longer answer.
Many IDEs -- including Visual Studio -- do NOT have the current directory as your project area. Instead, the current directory when you run your program is in a build area.
Imagine your project is in C:\Users\You\MyProgram. You put the file there (or in a subdirectory), and when you run your program from the IDE, you expect to find it. And it's not there.
That's because the IDE actually sets the current directory to somewhere else. (I'm not sure where -- I don't use Visual Studio.) If you do this:
cout << std::file_system::current_path() << endl;
it should give you a clue.

ifstream won't find file even though it's in the same directory

I've got a method to read a vector of bools from a file:
std::vector<bool> OPCConnector::getAlarmVector() {
std::vector<bool> data;
std::ifstream DataFile(filepath);
if (DataFile) {
bool value;
while (DataFile >> value) {
data.push_back(value);
std::cout << value;
}
}
return data;
}
The filepath variable is an object property that is assigned through the constructor:
OPCConnector::OPCConnector(std::string fpth) {
filepath = fpth;
}
And in the main() function, the constructor is called:
std::vector<bool> activations;
std::string filepath = "alarmes.txt";
OPCConnector opcc = OPCConnector(filepath);
activations = opcc.getAlarmVector();
Now, I've checked what the folder of the executable is via GetModuleFileNameA(), and I made sure that the file is in the same directory and has the same name (also, I made sure that the extension isn't part of the file name, like "alarmes.txt.txt").
I debugged the first method getAlarmVector() and it never gets past the if (DataFile) condition, as if it won't find file.
I run the code using Visual Studio 2019, and nothing happens. The vector remains empty. Error is No such file or directory.
Default working directory is $(ProjectDir) and it's exactly where my file is.
Edit: I've also tried using both relative and absolute paths, none work.
Edit 2: I've also checked the directory using GetCurrentDirectory() and copied the .txt file there too, and it isn't working.
SOLUTION: Strangely enough, I deleted the file and created it again with the same name, and it worked. Thanks for the answers.
My guess: your current working directory isn't what you think it is, especially if you're running from an IDE. I know of several IDEs where the current working directory is some build directory (it varies by IDE) unless you specifically change it.
I'm fairly sure Visual Studio is one such IDE.
Here's a tiny example program I wrote;
$ cat Foo.cpp
#include <iostream>
#include <fstream>
int main(int, char **) {
std::ifstream file { "Foo.cpp" };
if (file) {
std::cout << "File opened.\n";
}
else {
std::cout << "File not opened.\n";
}
}
Compile and run it:
$ g++ --std=c++17 Foo.cpp -o Foo && Foo
File opened.
Current folder and folder-of-exe-file are different things (sometimes). Try to specify full name of file (with disk, all folders, etc.).
You can check errors of file open operation by calling
if (!DataFile) { ... }
The std::filesystem library can help you resolve file and path related issues.
#include <filesystem>
// (in some function)
std::filesystem::path filepath = "alarmes.txt";
if ( !exists(filepath) )
{
std::cout << "File path " << filepath << " at absolute location "
<< absolute(filepath) << " does not exist\n";
}
See it on Compiler Explorer
You can get an error code (and get a description of error in internet) if you use C-function fopen. If open is failed, you get the nullptr as result of fopen and errno will contain code of error.

std::ifstream throwing error in Xcode 5

I'm trying to read in some GLSL source from simple plain text files using the following.
I provide the sources to the constructor like so:
Shader *shader = new Shader("res/default.vert", "res/default.frag");
The constructor passes those paths on directly to my little OSUtils file that reads in plain text and returns std::string:
Shader::Shader(const std::string& vertPath, const std::string& fragPath)
{
addVertexShader(vertPath);
addFragmentShader(fragPath);
}
void Shader::addVertexShader(const std::string& path)
{
try {
const std::string shaderSrc = OSUtils::fileToBuffer(path);
compileVertexShader(shaderSrc);
} catch (int e) {
std::cout << "Error no: " << e << std::endl;
exit(1);
}
}
The addFragmentShader function is nearly identical.
The fileToBuffer function is as such:
const std::string OSUtils::fileToBuffer(const std::string& path)
{
std::cout << "Trying to load: " << path << std::endl;
std::ifstream in(path, std::ios::in | std::ios::binary);
if (in)
{
std::string contents;
in.seekg(0, std::ios::end);
contents.resize(in.tellg());
in.seekg(0, std::ios::beg);
in.read(&contents[0], contents.size());
in.close();
return(contents);
}
throw(errno);
}
This throws the error with Errno 2 (No file found) in the addVertexShader function. And here's the kicker. I have this EXACT code running in a different project. Most of this code was copypasta from that earlier project that compiles just fine.
I'm running both projects in Xcode 5. The files themselves (res/default.vert and res/default.frag) have been added to the target and are listed in the Compile Sources listing under the target's Build Phases tab. In other words, everything seems in order, but I can't figure out why it won't load the files in this project. Is there some mysterious Xcode setting I'm missing? I'd suspect the code if it wasn't 100% identical to the code in the working project. The other project also mimics the file structure with both the .vert and .frag files living in a res/ folder. The only difference is that the source files that attempt to read the files live in a src/ subdirectory. My understanding though is that I should be able to reference the files relative to the project, not the actual source files themselves. Just in case, I tried "../res/default.vert" to no avail.
Any help is appreciated. Cheers!
The answer was in fact related to the working directory. There's a custom setting under the schemes that lets you set a custom working dir. Obviously not really suitable for distribution, but that explains the discrepancy. Thanks for the help!

Create a file at a given path using C++ in Linux

I want to create a file at a given path that is relative to the current directory. The following code behaves erratically. I some times see the file created and some times do not. That may be because of the change in the current directory. Here's the code.
//for appending timestamp
timeval ts;
gettimeofday(&ts,NULL);
std::string timestamp = boost::lexical_cast<std::string>(ts.tv_sec);
//./folder/inner_folder is an existing directory
std::string filename = "./folder/inner_folder/abc_"+timestamp+ ".csv";
std::ofstream output_file(filename);
output_file << "abc,efg";
output_file.close();
Now, the problem is the file is created only in some cases. That is when I have as a command line argument an input file from the current directory, it works fine.
./program input_file
If I have something like this, it does not work
./program ./folder1/input_file
I tried giving the full path as an argument for ofstream, I still don't see the files created.
What is the correct way to do this? Thanks
ofstream will not create missing directories in the file path, you must ensure the directories exist and if not create them using OS specific api or boost's file system library.
Always check the result of IO operations, and query system error code to determine reason for failures:
if (output_ file.is_open())
{
if (!(output_file << "abc,efg"))
{
// report error.
}
}
else
{
const int last_error = errno;
std::cerr << "failed to open "
<< filename
<< ": "
<< strerror(last_error)
<< std::endl;
}

How to "select" current directory?

I am writing a proportion calculator. At the beginning of the program, it loads a ascii text art picture from a .txt in the same folder.
Here is how I am doing it:
//Read picture
string line;
ifstream myfile("/Users/MYNAME/Desktop/MathScripts/Proportions/Value Finder/picture.txt");
if (myfile.is_open()) {
while (!myfile.eof()) {
getline(myfile, line);
cout << line << endl;
}
myfile.close();
} else cout << "Unable to load picture!!!" << endl;
//Finish reading txt
I heard how if the .txt is in the same folder, that you can just use the name and not have to say the directory. Meaning instead of
/Users/MYNAME/Desktop/MathScripts/Proportions/Value Finder/picture.txt
I could just use "picture.txt". That doesn't work for me, and I want the user to be able to move around the "Value Finder" folder without having to edit any code.
I am on Mac and I am using CodeRunner; anything odd?
Please do not tell me to make sure that picture.txt is in the same folder as my code. It is.
In order to open picture.txt without using a fully qualified path it has to reside in the current working directory. When an IDE launches an application it it sets the current working directory to the same one the application resides in. If picture.txt resides in a different directory than the application you will not be able to open it with just it's name. If you need to get the current working directory you can call getcwd like so.
char temp[MAXPATHLEN];
getcwd(temp, MAXPATHLEN);
If you want to allow the user to specify which directory picture.txt is in you can let them pass an argument on the command line. You can then create a fully qualified path with the supplied directory and the picture filename.
int main(int argc, const char **argv)
{
// Add some logic to see if the user passes a path as an argument
// and grab it. here we just assume it was passed on the command line.
const string user_path = arg[1];
//Read picture
string line;
ifstream myfile(user_path + "/picture.txt");
if (myfile.is_open())
{
while (!myfile.eof()) {
getline(myfile, line);
cout << line << endl;
}
myfile.close();
}
else
{
cout << "Unable to load picture!!!" << endl;
}
//Finish reading txt
return 0;
}
Now you can do something like this:
myapp "/user/USERNAME/Desktop/MathScripts/Proportions/Value Finder"
and it will look in that directory for the picture.txt file. (Quotes are required because there is a space in the pathname).
Note: You can call setcwd() to change the current working directory of the application.