2

I am creating a game engine in C++, and I had started adding support for loading files. I created a load file method that looks like this:

#include <string>
#include <fstream>

std::string read_file(const char* filepath) {

        FILE* file = fopen(filepath, "rt");
        if(file != NULL) {
            fseek(file, 0, SEEK_END);
        } else {
            std::string error_message = std::string("[ERROR]: Failed to load file ");
            error_message.append(filepath);
            return error_message;
        }
        unsigned long length = ftell(file);
        char* data = new char[length + 1];
        memset(data, 0, length + 1);
        fseek(file, 0, SEEK_SET);
        fread(data, 1, length, file);
        fclose(file);

        std::string result(data);
        delete[] data;

        return result;

    }

To test this function, I decided to try to load a "test.txt" file, but this will not work unless I include the full path, e.g.

"/Users/(Username)/.../(Executable Directory)/text.txt"

instead of just

"test.txt"

So, to fix this, I need a way to get the full path of the directory that the executable file is in, and then append the file name onto the end of the path, and load that in my load file method.

Anyone know how to get the path?

1 Answers1

6

Something like this will find you the executable on Windows/Ubuntu/Mac OS X:

#include <vector>

#include <boost/filesystem.hpp>

#if defined(_WIN32)
  #include <windows.h>
#elif defined(__linux__)
  #include <sstream>
  #include <unistd.h>
#elif defined(__APPLE__)
  #include <mach-o/dyld.h>
#endif

boost::filesystem::path find_executable()
{
  unsigned int bufferSize = 512;
  std::vector<char> buffer(bufferSize + 1);

#if defined(_WIN32)
  ::GetModuleFileName(NULL, &buffer[0], bufferSize);

#elif defined(__linux__)
  // Get the process ID.
  int pid = getpid();

  // Construct a path to the symbolic link pointing to the process executable.
  // This is at /proc/<pid>/exe on Linux systems (we hope).
  std::ostringstream oss;
  oss << "/proc/" << pid << "/exe";
  std::string link = oss.str();

  // Read the contents of the link.
  int count = readlink(link.c_str(), &buffer[0], bufferSize);
  if(count == -1) throw std::runtime_error("Could not read symbolic link");
  buffer[count] = '\0';

#elif defined(__APPLE__)
  if(_NSGetExecutablePath(&buffer[0], &bufferSize))
  {
    buffer.resize(bufferSize);
    _NSGetExecutablePath(&buffer[0], &bufferSize);
  }

#else
  #error Cannot yet find the executable on this platform
#endif

  std::string s = &buffer[0];
  return s;
}

From that, you can just use parent_path() to get the directory containing it.

One downside of this solution is that as it stands, you need to use Boost (and in particular Boost.Filesystem), but if that's not an issue for you then it should work well (for reference, this is the code I actually use for this on a day-to-day basis, and it does work well, at least for what I need). It could be adapted to remove the dependency on Boost.Filesystem without too much trouble if that is a problem.

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
  • Isn't the function called `find_executable()`? – Raven H. Apr 18 '17 at 05:00
  • @RavenH.: The function to find the executable is called `find_executable`. To get the directory containing it, you can call `parent_path()` on the path returned by a call to `find_executable`. – Stuart Golodetz Apr 18 '17 at 21:19