C/C++ Is there any cross plataform way of using relative paths?
I'm programming a little game in SDL and the files are structured like this:
"src/game/" have both .h and .cpp source files.
"data/" have the game files like开发者_如何转开发 maps, tilesets, sprites and so on...
to load a sprite for example I would use the following code.
spriteLib.loadSprite("data/sprites/sprite-ghost.bmp");
to convert this string to an absolute path I have those lines in the first 4 lines of the function:
SSprite CSpriteLib::loadSprite(std::string file)
{
//Converting the file path
char converted[128];
realpath(file.c_str(),converted);
file = converted;
But this way the program only compiles under liux so... if anyone knows another way to do that I would be grateful.
Boost is your friend. Here's a link to the Boost Filesystem tutorial.
Few points:
char converted[128];
realpath(file.c_str(),converted);
- First of all
realpath
on some operating systems (Solaris) may still return the relative path. - Your code contains buffer overflow, it is better to use
canonicalize_file_name
orchar *m=realpath(file.c_str(),0); ... free(m);
- however this is Linux specific. See manrealpath
to see how to use it more or less correctly.
Also how would realpath help you to open your data? If
fopen(converted,"r")
Works in your case then
fopen(file.c_str(),"r")
would work as well. It is provided to help removing all symbolic links, etc.
If you need some similar functionality to realpath
, you may use GetFullPathName
under Windows, but
it still behaves differently.
Just wrote a tiny class for it:
#include <string>
#include <vector>
#include <iostream>
class CFilePath
{
typedef std::vector<std::string> TPath;
public:
enum EPlatform
{
Windows,
Unix
};
CFilePath(EPlatform p_platform) : m_platform(p_platform) {}
CFilePath& operator/(const char* p_path)
{
m_path.push_back(p_path);
return *this;
}
std::string GetPath()
{
std::string ret;
if (m_path.empty())
return ret;
for (unsigned i = 0; i < m_path.size();)
{
ret+=m_path[i];
i++;
if (i < m_path.size())
{
switch (m_platform)
{
case Windows:
ret+="\\";
break;
case Unix:
ret+="/";
break;
}
}
}
return ret;
}
operator const char*()
{
return GetPath().c_str();
}
EPlatform m_platform;
private:
std::vector<std::string> m_path;
};
int main(int argc, char* argv[])
{
CFilePath::EPlatform platform = CFilePath::Windows; // variable
CFilePath path(platform);
path/"data"/"sprites"/"sprite-ghost.bmp";
std::cout << path << std::endl;
path.m_platform = CFilePath::Unix;
std::cout << path << std::endl;
return 0;
}
will prints:
data\sprites\sprite-ghost.bmp
data/sprites/sprite-ghost.bmp
My recommendation is: first, your main program should accept a native filename as an argument that represents where the data for the program live eg
C:\program files\mycompany\mygame
This passes the responsibility of deciding which data to use off to some other program (such as a bash script or driver or whatever).
Second, define the subparts of the directory space with the data in unix format so all filename literals in your program have this format. No ifs and buts, always Unix. Eg:
spriteLib.loadSprite("data/sprites/sprite-ghost.bmp");
Now write a function which combines the passed in directory name (in native format) with the unix filename translated to native format to get a suitable filename for access. You have already tried to do this but you're using an OS dependent function. Don't. Write it yourself. You want to get this out:
C:\program files\mycompany\mygame\data\sprites\sprite-ghost.bmp
The method of @Industrial-antidepressant (previously @nice blonde stupid girl) is quite good too, but it's a bit more work.
精彩评论