Multiprocess c++(11) with linked list pointer as global variable - c++

I have the classic problem as given here, here and here and also here,
However, I would like a child process to insert an element at the end of a doubly linked list. a point to the first element of the list is global, and I want to access all of the list elements form the main process, and also next time I branch the main using fork, I want to be abe to access all elements, and update them, and insert more elements, in turn the main process again being able to access the modifyied list.
Each process exits with a system call with execvp (i need them to be able to call stuff using varied number of arameters).
I perhaps am asking a too broad question, but I personally did not get any further than branching and the inserting an element at the end of the list. Thus I actually dont have a single line of code that takes me where I want to go. I have no idea how to use shm() in this scenario.
Please help.

You can try this.. I just wrote it from scratch.. It is cross-platform so that's always a plus. The allocators and pools can be re-used with anything. For example, you can make any stl container allocate on the stack or wherever you want..
SharedMemory.hpp:
#ifndef SHAREDMEMORY_HPP_INCLUDED
#define SHAREDMEMORY_HPP_INCLUDED
#if defined _WIN32 || defined _WIN64
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include <string>
#include <cstdint>
class SharedMemory
{
private:
std::string name;
std::size_t size;
void* data;
void* hFileMap;
public:
SharedMemory(std::string name, std::size_t size) : name(name), size(size), data(nullptr), hFileMap(nullptr) {};
~SharedMemory();
bool Open();
bool Create();
std::size_t GetSize() const {return this->size;}
void* GetPointer() const {return this->data;}
};
#endif // SHAREDMEMORY_HPP_INCLUDED
SharedMemory.cpp:
#include "SharedMemory.hpp"
SharedMemory::~SharedMemory()
{
if (data)
{
#if defined _WIN32 || defined _WIN64
UnmapViewOfFile(data);
data = nullptr;
if (hFileMap)
{
if (CloseHandle(hFileMap))
{
hFileMap = nullptr;
}
}
#else
if (data)
{
munmap(data, size);
data = nullptr;
}
if (hFileMap)
{
if (!close(hFileMap))
{
hFileMap = nullptr;
}
}
#endif
}
}
bool SharedMemory::Open()
{
#if defined _WIN32 || defined _WIN64
if ((hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, name.c_str())) == nullptr)
{
return false;
}
if ((data = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == nullptr)
{
CloseHandle(hFileMap);
return false;
}
#else
if ((hFileMap = open(MapName.c_str(), O_RDWR | O_CREAT, 438)) == -1)
{
return false;
}
if ((data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
{
close(hFileMap);
return false;
}
#endif
return true;
}
bool SharedMemory::Create()
{
#if defined _WIN32 || defined _WIN64
if ((hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size, name.c_str())) == nullptr)
{
return false;
}
if ((data = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == nullptr)
{
CloseHandle(hFileMap);
return false;
}
#else
if ((hFileMap = open(MapName.c_str(), O_RDWR | O_CREAT, 438)) == -1)
{
return false;
}
if ((data = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
{
close(hFileMap);
return false;
}
#endif
return true;
}
Pools.hpp:
#ifndef POOLS_HPP_INCLUDED
#define POOLS_HPP_INCLUDED
#include <stdexcept>
#include <cstdint>
#include "SharedMemory.hpp"
template<typename T>
class SharedPool
{
private:
T* data;
SharedMemory* shm;
std::size_t size;
public:
SharedPool(SharedMemory* shm) : data(reinterpret_cast<T*>(shm->GetPointer())), shm(shm), size(shm->GetSize()) {};
template<typename U = T>
void* allocate(std::size_t n, const void* hint = 0) {return &data[0];}
template<typename U = T>
void deallocate(U* ptr, std::size_t n) {}
template<typename U = T>
std::size_t max_size() const {return size;}
};
#endif // POOLS_HPP_INCLUDED
main.cpp (adding values to shared memory from process one):
#include "SharedMemory.hpp"
#include "Allocators.hpp"
#include "Pools.hpp"
#include <vector>
#include <iostream>
int main()
{
SharedMemory mem = SharedMemory("Local\\Test_Shared_Memory", 1024);
if (!mem.Open() && !mem.Create())
{
throw std::runtime_error("Error Mapping Shared Memory!");
}
auto pool = PoolAllocator<int, SharedPool<int>>(SharedPool<int>(&mem));
std::vector<int, decltype(pool)> v(pool);
int* ptr = reinterpret_cast<int*>(mem.GetPointer());
std::cout<<"Pushing 3 values to: "<<ptr<<"\n";
v.push_back(100);
v.push_back(200);
v.push_back(700);
std::cin.get();
}
main.cpp (Reading values from shared memory process two):
#include "SharedMemory.hpp"
#include "Allocators.hpp"
#include "Pools.hpp"
#include <vector>
#include <iostream>
int main()
{
SharedMemory mem = SharedMemory("Local\\Test_Shared_Memory", 1024);
if (!mem.Open() && !mem.Create())
{
throw std::runtime_error("Error Mapping Shared Memory!");
}
auto pool = PoolAllocator<int, SharedPool<int>>(SharedPool<int>(&mem));
std::vector<int, decltype(pool)> v(pool);
int* ptr = reinterpret_cast<int*>(mem.GetPointer());
std::cout<<"Reading 3 values from: "<<ptr<<"\n";
v.reserve(3);
std::cout<<v[0]<<"\n";
std::cout<<v[1]<<"\n";
std::cout<<v[2]<<"\n";
std::cin.get();
}

This is a tough problem.
One way is to use Shared Memory and when you build the linked list, give it your own allocator, to use the shared memory. Other way is to implement your own linked list based on the shared memory.
You can also try to use boost - Boost interprocess, which is probably the ideal solution.
Specifically speaking - interprocess with containers

Related

Post semaphore in shared memory crashes

I have 2 processes:
producer: creates an Transaction object inside a shared memory then waiting for consumer to read the data. The waiting is done via a semaphore which is a member of Transaction
consumer: reads the Transaction object created by producer and tells producer the reading is done by posting semaphore in Transaction object.
Consumer is able to read the data from shared memory but it crashes when it tries to post semaphore.
What's wrong with this implementation?
//producer
#include <string>
#include <semaphore.h>
#include <cstddef>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <unistd.h>
#include <iostream>
constexpr char g_shared_mem_name[]="/our_shared_mem_new";
class Transaction
{
public:
virtual ~Transaction() = default;
Transaction(const uint32_t f_count) : m_count{f_count}{}
sem_t m_sem;
uint32_t m_count{0};
};
void* createSharedMem(const char *f_name, const size_t f_size) {
int fd = shm_open(f_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd != -1) {
// A new shared memory object initially has zero length.
// The size of the object can be set using ftruncate.
// The newly allocated bytes of a shared memory object are
// automatically initialized to 0
if (ftruncate(fd, f_size) == -1) {
return nullptr;
}
return mmap(NULL, f_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
}
else {
return nullptr;
}
}
int main()
{
void * shared_mem = createSharedMem(g_shared_mem_name, sizeof(Transaction));
if ( shared_mem )
{
Transaction* shared_obj = new(shared_mem) Transaction(2022);
if( sem_init(&shared_obj->m_sem,1,0) == -1) {
std::cerr << "Unable to initialize m_sem_init\n";
}
//waiting for consumer to finish reading data
if( sem_wait(&shared_obj->m_sem) == -1) {
std::cerr << "Error on waiting for semaphore\n";
}
}
else {
std::cerr << "Unable to create shared object\n";
}
shm_unlink(g_shared_mem_name);
return EXIT_SUCCESS;
}
Consumer
#include <string>
#include <semaphore.h>
#include <cstddef>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <unistd.h>
#include <iostream>
constexpr char g_shared_mem_name[]="/our_shared_mem_new";
class Transaction
{
public:
virtual ~Transaction() = default;
Transaction(const uint32_t f_count) : m_count{f_count}{}
sem_t m_sem;
uint32_t m_count{0};
};
void* openSharedMem(const char * f_name, const size_t f_size) {
int fd = shm_open(f_name, O_RDONLY, 0);
if( fd != -1 ) {
return mmap(NULL, f_size, PROT_READ, MAP_SHARED, fd, 0 );
}
else {
return nullptr;
}
}
int main()
{
void* shared_mem = openSharedMem(g_shared_mem_name, sizeof(Transaction));
if (shared_mem) {
Transaction * m_inst = (Transaction*)shared_mem;
std::cout << "value of cnt:" << m_inst->m_count << std::endl;
//crash here
if (sem_post(&m_inst->m_sem) == -1) {
std::cerr << "Unable to post semaphore\n";
}
}
else {
std::cerr << "Unable to open shared object\n";
}
return EXIT_SUCCESS;
}
Output
value of cnt:2022
Segmentation fault (core dumped)

process shared pthread_cond_t doesn't work in gcc 8.4 but works in gcc 4.8.5

Recently I upgraded my OS from RHEL 7.6(gcc 4.8.5) to RHEL 8.4(gcc 8.4) and I'm facing issues related to process synchronization using pthread_mutex_t and pthread_cond_t. The reason I'm not using C++ std::mutex and std::condition_variable is because they doesn't support synchronization between processes. This used to work well in gcc 4.8.5 but not in gcc 8.4. This is my code
Binary_Semaphore.h
#ifndef BINARY_SEMAPHORE_H
#define BINARY_SEMAPHORE_H
#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
struct binary_semaphore_attr {
pthread_mutex_t mutex;
pthread_cond_t cvar;
bool flag;
};
class Binary_Semaphore {
struct binary_semaphore_attr *bin_sem_attr;
const std::string bin_sem_attr_shm_ID;
const bool is_process_shared;
const bool is_to_be_created;
public:
Binary_Semaphore(const std::string& bin_sem_attr_shm_ID, const bool is_process_shared, const bool is_to_be_created);
~Binary_Semaphore();
void post();
void wait();
template<typename T>
static void create_shared_memory(T **shm, const std::string& shm_ID, const bool is_to_be_created, const int o_flags, const int mode) {
int shm_fd;
if ((shm_fd = shm_open(shm_ID.c_str(), o_flags, mode)) == -1) {
std::cerr << "shm_open failed with " << shm_ID << "\n";
exit(EXIT_FAILURE);
}
if (is_to_be_created) {
if (ftruncate(shm_fd, sizeof(T)) == -1) {
std::cerr << "ftruncate failed with " << shm_ID << "\n";
exit(EXIT_FAILURE);
}
}
if ((*shm = reinterpret_cast<T*>(mmap(nullptr, sizeof(T), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0))) == MAP_FAILED) {
std::cerr << "mmap failed with " << shm_ID << "\n";
exit(EXIT_FAILURE);
}
close(shm_fd);
}
};
#endif
Binary_Semaphore.cpp
#include "Binary_Semaphore.h"
Binary_Semaphore::Binary_Semaphore(const std::string& bin_sem_attr_shm_ID, const bool is_process_shared, const bool is_to_be_created) : bin_sem_attr_shm_ID(bin_sem_attr_shm_ID), is_process_shared(is_process_shared), is_to_be_created(is_to_be_created) {
/* set binary semaphore attribute */
if (is_to_be_created) {
if (is_process_shared) {
create_shared_memory(&bin_sem_attr, bin_sem_attr_shm_ID, is_to_be_created, O_CREAT | O_RDWR | O_TRUNC, S_IRWXU | S_IRWXG);
/* set mutex shared between processes */
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&bin_sem_attr->mutex, &mutex_attr);
pthread_mutexattr_destroy(&mutex_attr);
/* set cvar shared between processes */
pthread_condattr_t cvar_attr;
pthread_condattr_init(&cvar_attr);
pthread_condattr_setpshared(&cvar_attr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&bin_sem_attr->cvar, &cvar_attr);
pthread_condattr_destroy(&cvar_attr);
} else
bin_sem_attr = new binary_semaphore_attr();
} else {
if (is_process_shared)
create_shared_memory(&bin_sem_attr, bin_sem_attr_shm_ID, is_to_be_created, O_RDWR, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP);
}
}
Binary_Semaphore::~Binary_Semaphore() {
if (is_to_be_created) {
pthread_mutex_destroy(&bin_sem_attr->mutex);
pthread_cond_destroy(&bin_sem_attr->cvar);
}
if (is_process_shared) {
munmap(bin_sem_attr, sizeof(binary_semaphore_attr));
shm_unlink(bin_sem_attr_shm_ID.c_str());
}
}
void Binary_Semaphore::post() {
if (pthread_mutex_lock(&bin_sem_attr->mutex) == EOWNERDEAD)
pthread_mutex_consistent(&bin_sem_attr->mutex);
bin_sem_attr->flag = true;
pthread_mutex_unlock(&bin_sem_attr->mutex);
pthread_cond_signal(&bin_sem_attr->cvar);
}
void Binary_Semaphore::wait() {
if (pthread_mutex_lock(&bin_sem_attr->mutex) == EOWNERDEAD)
pthread_mutex_consistent(&bin_sem_attr->mutex);
while (!bin_sem_attr->flag) {
if (pthread_cond_wait(&bin_sem_attr->cvar, &bin_sem_attr->mutex) == EOWNERDEAD)
pthread_mutex_consistent(&bin_sem_attr->mutex);
}
bin_sem_attr->flag = false;
pthread_mutex_unlock(&bin_sem_attr->mutex);
}
First_Process.cpp
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include "Binary_Semaphore.h"
int main() {
static const std::string BSEM = R"(/BSEM)";
Binary_Semaphore *binary_sem = new Binary_Semaphore(BSEM, true, true);
while (true) {
binary_sem->post();
std::cout << "signal posted" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1LL));
}
}
Second_Process.cpp
#include <iostream>
#include <string>
#include "Binary_Semaphore.h"
int main() {
static const std::string BSEM = R"(/BSEM)";
Binary_Semaphore *binary_sem = new Binary_Semaphore(BSEM, true, false);
while (true) {
binary_sem->wait();
std::cout << "signal received" << std::endl;
}
}
Run first process followed by second process and then abruptly terminate second process using Ctrl^C and then rerun second process, no more prints on the terminal(both first process and second process).
Did anybody face same kind of issue with latest gcc versions?
glibc does not support robust condition variables. (They are not part of POSIX.) You need to re-create the shared memory segment with the condition variables if one of the participating processes terminates abnormally.

How to run a C++ exec file from the directory where it was called? [duplicate]

I know this question has been asked before but I still haven't seen a satisfactory answer, or a definitive "no, this cannot be done", so I'll ask again!
All I want to do is get the path to the currently running executable, either as an absolute path or relative to where the executable is invoked from, in a platform-independent fashion. I though boost::filesystem::initial_path was the answer to my troubles but that seems to only handle the 'platform-independent' part of the question - it still returns the path from which the application was invoked.
For a bit of background, this is a game using Ogre, which I'm trying to profile using Very Sleepy, which runs the target executable from its own directory, so of course on load the game finds no configuration files etc. and promptly crashes. I want to be able to pass it an absolute path to the configuration files, which I know will always live alongside the executable. The same goes for debugging in Visual Studio - I'd like to be able to run $(TargetPath) without having to set the working directory.
There is no cross platform way that I know.
For Linux: pass "/proc/self/exe" to std::filesystem::canonical or readlink.
Windows: pass NULL as the module handle to GetModuleFileName.
The boost::dll::program_location function is one of the best cross platform methods of getting the path of the running executable that I know of. The DLL library was added to Boost in version 1.61.0.
The following is my solution. I have tested it on Windows, Mac OS X, Solaris, Free BSD, and GNU/Linux.
It requires Boost 1.55.0 or greater. It uses the Boost.Filesystem library directly and the Boost.Locale library and Boost.System library indirectly.
src/executable_path.cpp
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
# include <boost/process.hpp>
#endif
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
# include <Windows.h>
#endif
#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>
namespace boost {
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector buf(1024, 0);
size_type size = buf.size();
bool havePath = false;
bool shouldContinue = true;
do
{
DWORD result = GetModuleFileNameA(nullptr, &buf[0], size);
DWORD lastError = GetLastError();
if (result == 0)
{
shouldContinue = false;
}
else if (result < size)
{
havePath = true;
shouldContinue = false;
}
else if (
result == size
&& (lastError == ERROR_INSUFFICIENT_BUFFER || lastError == ERROR_SUCCESS)
)
{
size *= 2;
buf.resize(size);
}
else
{
shouldContinue = false;
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
// On Microsoft Windows, there is no need to call boost::filesystem::canonical or
// boost::filesystem::path::make_preferred. The path returned by GetModuleFileNameA
// is the one we want.
std::string ret = &buf[0];
return ret;
}
#elif (BOOST_OS_MACOS)
# include <mach-o/dyld.h>
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
char_vector buf(1024, 0);
uint32_t size = static_cast<uint32_t>(buf.size());
bool havePath = false;
bool shouldContinue = true;
do
{
int result = _NSGetExecutablePath(&buf[0], &size);
if (result == -1)
{
buf.resize(size + 1);
std::fill(std::begin(buf), std::end(buf), 0);
}
else
{
shouldContinue = false;
if (buf.at(0) != 0)
{
havePath = true;
}
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#elif (BOOST_OS_SOLARIS)
# include <stdlib.h>
std::string executable_path(const char* argv0)
{
std::string ret = getexecname();
if (ret.empty())
{
return detail::executable_path_fallback(argv0);
}
boost::filesystem::path p(ret);
if (!p.has_root_directory())
{
boost::system::error_code ec;
p = boost::filesystem::canonical(
p, boost::filesystem::current_path(), ec);
if (ec.value() != boost::system::errc::success)
{
return detail::executable_path_fallback(argv0);
}
ret = p.make_preferred().string();
}
return ret;
}
#elif (BOOST_OS_BSD)
# include <sys/sysctl.h>
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
int mib[4]{0};
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
int result = sysctl(mib, 4, nullptr, &size, nullptr, 0);
if (-1 == result)
{
return detail::executable_path_fallback(argv0);
}
char_vector buf(size + 1, 0);
result = sysctl(mib, 4, &buf[0], &size, nullptr, 0);
if (-1 == result)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#elif (BOOST_OS_LINUX)
# include <unistd.h>
std::string executable_path(const char *argv0)
{
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector buf(1024, 0);
size_type size = buf.size();
bool havePath = false;
bool shouldContinue = true;
do
{
ssize_t result = readlink("/proc/self/exe", &buf[0], size);
if (result < 0)
{
shouldContinue = false;
}
else if (static_cast<size_type>(result) < size)
{
havePath = true;
shouldContinue = false;
size = result;
}
else
{
size *= 2;
buf.resize(size);
std::fill(std::begin(buf), std::end(buf), 0);
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#else
std::string executable_path(const char *argv0)
{
return detail::executable_path_fallback(argv0);
}
#endif
}
src/detail/executable_path_internals.cpp
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
# include <boost/process.hpp>
#endif
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
# include <Windows.h>
#endif
#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>
namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName)
{
if (varName.empty()) return "";
#if (BOOST_OS_BSD || BOOST_OS_CYGWIN || BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_SOLARIS)
char* value = std::getenv(varName.c_str());
if (!value) return "";
return value;
#elif (BOOST_OS_WINDOWS)
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector value(8192, 0);
size_type size = value.size();
bool haveValue = false;
bool shouldContinue = true;
do
{
DWORD result = GetEnvironmentVariableA(varName.c_str(), &value[0], size);
if (result == 0)
{
shouldContinue = false;
}
else if (result < size)
{
haveValue = true;
shouldContinue = false;
}
else
{
size *= 2;
value.resize(size);
}
} while (shouldContinue);
std::string ret;
if (haveValue)
{
ret = &value[0];
}
return ret;
#else
return "";
#endif
}
bool GetDirectoryListFromDelimitedString(
const std::string& str,
std::vector<std::string>& dirs)
{
typedef boost::char_separator<char> char_separator_type;
typedef boost::tokenizer<
boost::char_separator<char>, std::string::const_iterator,
std::string> tokenizer_type;
dirs.clear();
if (str.empty())
{
return false;
}
#if (BOOST_OS_WINDOWS)
const std::string os_pathsep(";");
#else
const std::string os_pathsep(":");
#endif
char_separator_type pathSep(os_pathsep.c_str());
tokenizer_type strTok(str, pathSep);
typename tokenizer_type::iterator strIt;
typename tokenizer_type::iterator strEndIt = strTok.end();
for (strIt = strTok.begin(); strIt != strEndIt; ++strIt)
{
dirs.push_back(*strIt);
}
if (dirs.empty())
{
return false;
}
return true;
}
std::string search_path(const std::string& file)
{
if (file.empty()) return "";
std::string ret;
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
{
namespace bp = boost::process;
boost::filesystem::path p = bp::search_path(file);
ret = p.make_preferred().string();
}
#endif
if (!ret.empty()) return ret;
// Drat! I have to do it the hard way.
std::string pathEnvVar = GetEnv("PATH");
if (pathEnvVar.empty()) return "";
std::vector<std::string> pathDirs;
bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs);
if (!getDirList) return "";
std::vector<std::string>::const_iterator it = pathDirs.cbegin();
std::vector<std::string>::const_iterator itEnd = pathDirs.cend();
for ( ; it != itEnd; ++it)
{
boost::filesystem::path p(*it);
p /= file;
if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
{
return p.make_preferred().string();
}
}
return "";
}
std::string executable_path_fallback(const char *argv0)
{
if (argv0 == nullptr) return "";
if (argv0[0] == 0) return "";
#if (BOOST_OS_WINDOWS)
const std::string os_sep("\\");
#else
const std::string os_sep("/");
#endif
if (strstr(argv0, os_sep.c_str()) != nullptr)
{
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
argv0, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
}
std::string ret = search_path(argv0);
if (!ret.empty())
{
return ret;
}
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
argv0, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
ret = p.make_preferred().string();
}
return ret;
}
}
}
include/boost/executable_path.hpp
#ifndef BOOST_EXECUTABLE_PATH_HPP_
#define BOOST_EXECUTABLE_PATH_HPP_
#pragma once
#include <string>
namespace boost {
std::string executable_path(const char * argv0);
}
#endif // BOOST_EXECUTABLE_PATH_HPP_
include/boost/detail/executable_path_internals.hpp
#ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#pragma once
#include <string>
#include <vector>
namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName);
bool GetDirectoryListFromDelimitedString(
const std::string& str,
std::vector<std::string>& dirs);
std::string search_path(const std::string& file);
std::string executable_path_fallback(const char * argv0);
}
}
#endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
I have a complete project, including a test application and CMake build files available at SnKOpen - /cpp/executable_path/trunk. This version is more complete than the version I provided here. It is also supports more platforms.
I have tested the application on all supported operating systems in the following four scenarios.
Relative path, executable in current directory: i.e. ./executable_path_test
Relative path, executable in another directory: i.e. ./build/executable_path_test
Full path: i.e. /some/dir/executable_path_test
Executable in path, file name only: i.e. executable_path_test
In all four scenarios, both the executable_path and executable_path_fallback functions work and return the same results.
Notes
This is an updated answer to this question. I updated the answer to take into consideration user comments and suggestions. I also added a link to a project in my SVN Repository.
This way uses boost + argv. You mentioned this may not be cross platform because it may or may not include the executable name. Well the following code should work around that.
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc,char** argv)
{
fs::path full_path( fs::initial_path<fs::path>() );
full_path = fs::system_complete( fs::path( argv[0] ) );
std::cout << full_path << std::endl;
//Without file name
std::cout << full_path.stem() << std::endl;
//std::cout << fs::basename(full_path) << std::endl;
return 0;
}
The following code gets the current working directory which may do what you need
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc,char** argv)
{
//current working directory
fs::path full_path( fs::current_path<fs::path>() );
std::cout << full_path << std::endl;
std::cout << full_path.stem() << std::endl;
//std::cout << fs::basepath(full_path) << std::endl;
return 0;
}
Note
Just realized that basename() was deprecated so had to switch to .stem()
C++17, windows, unicode, using filesystem new api:
#include "..\Project.h"
#include <filesystem>
using namespace std;
using namespace filesystem;
int wmain(int argc, wchar_t** argv)
{
auto dir = weakly_canonical(path(argv[0])).parent_path();
printf("%S", dir.c_str());
return 0;
}
(Important: Use wmain with wchar_t** - don't mix main with wchar_t**. For cmake projects enable unicode using add_definitions(-DUNICODE -D_UNICODE)).
Suspect this solution should be portable, but don't know how unicode is implemented on other OS's.
weakly_canonical is needed only if you use as Output Directory upper folder references ('..') to simplify path. If you don't use it - remove it.
If you're operating from dynamic link library (.dll /.so), then you might not have argv, then you can consider following solution:
application.h:
#pragma once
//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>
namespace std {
namespace filesystem = experimental::filesystem;
}
#endif
std::filesystem::path getexepath();
application.cpp:
#include "application.h"
#ifdef _WIN32
#include <windows.h> //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h> //readlink
#endif
std::filesystem::path getexepath()
{
#ifdef _WIN32
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(NULL, path, MAX_PATH);
return path;
#else
char result[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
#endif
}
I'm not sure about Linux, but try this for Windows:
#include <windows.h>
#include <iostream>
using namespace std ;
int main()
{
char ownPth[MAX_PATH];
// When NULL is passed to GetModuleHandle, the handle of the exe itself is returned
HMODULE hModule = GetModuleHandle(NULL);
if (hModule != NULL)
{
// Use GetModuleFileName() with module handle to get the path
GetModuleFileName(hModule, ownPth, (sizeof(ownPth)));
cout << ownPth << endl ;
system("PAUSE");
return 0;
}
else
{
cout << "Module handle is NULL" << endl ;
system("PAUSE");
return 0;
}
}
This is what I ended up with
The header file looks like this:
#pragma once
#include <string>
namespace MyPaths {
std::string getExecutablePath();
std::string getExecutableDir();
std::string mergePaths(std::string pathA, std::string pathB);
bool checkIfFileExists (const std::string& filePath);
}
Implementation
#if defined(_WIN32)
#include <windows.h>
#include <Shlwapi.h>
#include <io.h>
#define access _access_s
#endif
#ifdef __APPLE__
#include <libgen.h>
#include <limits.h>
#include <mach-o/dyld.h>
#include <unistd.h>
#endif
#ifdef __linux__
#include <limits.h>
#include <libgen.h>
#include <unistd.h>
#if defined(__sun)
#define PROC_SELF_EXE "/proc/self/path/a.out"
#else
#define PROC_SELF_EXE "/proc/self/exe"
#endif
#endif
namespace MyPaths {
#if defined(_WIN32)
std::string getExecutablePath() {
char rawPathName[MAX_PATH];
GetModuleFileNameA(NULL, rawPathName, MAX_PATH);
return std::string(rawPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char* exePath = new char[executablePath.length()];
strcpy(exePath, executablePath.c_str());
PathRemoveFileSpecA(exePath);
std::string directory = std::string(exePath);
delete[] exePath;
return directory;
}
std::string mergePaths(std::string pathA, std::string pathB) {
char combined[MAX_PATH];
PathCombineA(combined, pathA.c_str(), pathB.c_str());
std::string mergedPath(combined);
return mergedPath;
}
#endif
#ifdef __linux__
std::string getExecutablePath() {
char rawPathName[PATH_MAX];
realpath(PROC_SELF_EXE, rawPathName);
return std::string(rawPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char *executablePathStr = new char[executablePath.length() + 1];
strcpy(executablePathStr, executablePath.c_str());
char* executableDir = dirname(executablePathStr);
delete [] executablePathStr;
return std::string(executableDir);
}
std::string mergePaths(std::string pathA, std::string pathB) {
return pathA+"/"+pathB;
}
#endif
#ifdef __APPLE__
std::string getExecutablePath() {
char rawPathName[PATH_MAX];
char realPathName[PATH_MAX];
uint32_t rawPathSize = (uint32_t)sizeof(rawPathName);
if(!_NSGetExecutablePath(rawPathName, &rawPathSize)) {
realpath(rawPathName, realPathName);
}
return std::string(realPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char *executablePathStr = new char[executablePath.length() + 1];
strcpy(executablePathStr, executablePath.c_str());
char* executableDir = dirname(executablePathStr);
delete [] executablePathStr;
return std::string(executableDir);
}
std::string mergePaths(std::string pathA, std::string pathB) {
return pathA+"/"+pathB;
}
#endif
bool checkIfFileExists (const std::string& filePath) {
return access( filePath.c_str(), 0 ) == 0;
}
}
For windows:
GetModuleFileName - returns the exe path + exe filename
To remove filename
PathRemoveFileSpec
QT provides this with OS abstraction as QCoreApplication::applicationDirPath()
If using C++17 one can do the following to get the path to the executable.
#include <filesystem>
std::filesystem::path getExecutablePath()
{
return std::filesystem::canonical("/proc/self/exe");
}
The above answer has been tested on Debian 10 using G++ 9.3.0
This is a Windows specific way, but it is at least half of your answer.
GetThisPath.h
/// dest is expected to be MAX_PATH in length.
/// returns dest
/// TCHAR dest[MAX_PATH];
/// GetThisPath(dest, MAX_PATH);
TCHAR* GetThisPath(TCHAR* dest, size_t destSize);
GetThisPath.cpp
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
TCHAR* GetThisPath(TCHAR* dest, size_t destSize)
{
if (!dest) return NULL;
if (MAX_PATH > destSize) return NULL;
DWORD length = GetModuleFileName( NULL, dest, destSize );
PathRemoveFileSpec(dest);
return dest;
}
mainProgram.cpp
TCHAR dest[MAX_PATH];
GetThisPath(dest, MAX_PATH);
I would suggest using platform detection as preprocessor directives to change the implementation of a wrapper function that calls GetThisPath for each platform.
Using args[0] and looking for '/' (or '\\'):
#include <string>
#include <iostream> // to show the result
int main( int numArgs, char *args[])
{
// Get the last position of '/'
std::string aux(args[0]);
// get '/' or '\\' depending on unix/mac or windows.
#if defined(_WIN32) || defined(WIN32)
int pos = aux.rfind('\\');
#else
int pos = aux.rfind('/');
#endif
// Get the path and the name
std::string path = aux.substr(0,pos+1);
std::string name = aux.substr(pos+1);
// show results
std::cout << "Path: " << path << std::endl;
std::cout << "Name: " << name << std::endl;
}
EDITED:
If '/' does not exist, pos==-1 so the result is correct.
For Windows you can use GetModuleFilename().
For Linux see BinReloc (old, defunct URL) mirror of BinReloc in datenwolf's GitHub repositories.
This is probably the most natural way to do it, while covering most major desktop platforms. I am not certain, but I believe this should work with all the BSD's, not just FreeBSD, if you change the platform macro check to cover all of them. If I ever get around to installing Solaris, I'll be sure to add that platform to the supported list.
Features full UTF-8 support on Windows, which not everyone cares enough to go that far.
procinfo/win32/procinfo.cpp
#ifdef _WIN32
#include "../procinfo.h"
#include <windows.h>
#include <tlhelp32.h>
#include <cstddef>
#include <vector>
#include <cwchar>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
static inline string narrow(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
process_t ppid_from_pid(process_t pid) {
process_t ppid;
HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hp, &pe)) {
do {
if (pe.th32ProcessID == pid) {
ppid = pe.th32ParentProcessID;
break;
}
} while (Process32Next(hp, &pe));
}
CloseHandle(hp);
return ppid;
}
string path_from_pid(process_t pid) {
string path;
HANDLE hm = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
MODULEENTRY32W me = { 0 };
me.dwSize = sizeof(MODULEENTRY32W);
if (Module32FirstW(hm, &me)) {
do {
if (me.th32ProcessID == pid) {
path = narrow(me.szExePath);
break;
}
} while (Module32NextW(hm, &me));
}
CloseHandle(hm);
return path;
}
#endif
procinfo/macosx/procinfo.cpp
#if defined(__APPLE__) && defined(__MACH__)
#include "../procinfo.h"
#include <libproc.h>
using std::string;
string path_from_pid(process_t pid) {
string path;
char buffer[PROC_PIDPATHINFO_MAXSIZE];
if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) {
path = string(buffer) + "\0";
}
return path;
}
#endif
procinfo/linux/procinfo.cpp
#ifdef __linux__
#include "../procinfo.h"
#include <cstdlib>
using std::string;
using std::to_string;
string path_from_pid(process_t pid) {
string path;
string link = string("/proc/") + to_string(pid) + string("/exe");
char *buffer = realpath(link.c_str(), NULL);
path = buffer ? : "";
free(buffer);
return path;
}
#endif
procinfo/freebsd/procinfo.cpp
#ifdef __FreeBSD__
#include "../procinfo.h"
#include <sys/sysctl.h>
#include <cstddef>
using std::string;
using std::size_t;
string path_from_pid(process_t pid) {
string path;
size_t length;
// CTL_KERN::KERN_PROC::KERN_PROC_PATHNAME(pid)
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid };
if (sysctl(mib, 4, NULL, &length, NULL, 0) == 0) {
path.resize(length, '\0');
char *buffer = path.data();
if (sysctl(mib, 4, buffer, &length, NULL, 0) == 0) {
path = string(buffer) + "\0";
}
}
return path;
}
#endif
procinfo/procinfo.cpp
#include "procinfo.h"
#ifdef _WiN32
#include <process.h>
#endif
#include <unistd.h>
#include <cstddef>
using std::string;
using std::size_t;
process_t pid_from_self() {
#ifdef _WIN32
return _getpid();
#else
return getpid();
#endif
}
process_t ppid_from_self() {
#ifdef _WIN32
return ppid_from_pid(pid_from_self());
#else
return getppid();
#endif
}
string dir_from_pid(process_t pid) {
string fname = path_from_pid(pid);
size_t fp = fname.find_last_of("/\\");
return fname.substr(0, fp + 1);
}
string name_from_pid(process_t pid) {
string fname = path_from_pid(pid);
size_t fp = fname.find_last_of("/\\");
return fname.substr(fp + 1);
}
procinfo/procinfo.h
#ifdef _WiN32
#include <windows.h>
typedef DWORD process_t;
#else
#include <sys/types.h>
typedef pid_t process_t;
#endif
#include <string>
/* windows-only helper function */
process_t ppid_from_pid(process_t pid);
/* get current process process id */
process_t pid_from_self();
/* get parent process process id */
process_t ppid_from_self();
/* std::string possible_result = "C:\\path\\to\\file.exe"; */
std::string path_from_pid(process_t pid);
/* std::string possible_result = "C:\\path\\to\\"; */
std::string dir_from_pid(process_t pid);
/* std::string possible_result = "file.exe"; */
std::string name_from_pid(process_t pid);
This allows getting the full path to the executable of pretty much any process id, except on Windows there are some process's with security attributes which simply will not allow it, so wysiwyg, this solution is not perfect.
To address what the question was asking more precisely, you may do this:
procinfo.cpp
#include "procinfo/procinfo.h"
#include <iostream>
using std::string;
using std::cout;
using std::endl;
int main() {
cout << dir_from_pid(pid_from_self()) << endl;
return 0;
}
Build the above file structure with this command:
procinfo.sh
cd "${0%/*}"
g++ procinfo.cpp procinfo/procinfo.cpp procinfo/win32/procinfo.cpp procinfo/macosx/procinfo.cpp procinfo/linux/procinfo.cpp procinfo/freebsd/procinfo.cpp -o procinfo.exe
For downloading a copy of the files listed above:
git clone git://github.com/time-killer-games/procinfo.git
For more cross-platform process-related goodness:
https://github.com/time-killer-games/enigma-dev
See the readme for a list of most of the functions included.
As others mentioned, argv[0] is quite a nice solution, provided that the platform actually passes the executable path, which is surely not less probable than the OS being Windows (where WinAPI can help find the executable path). If you want to strip the string to only include the path to the directory where the executable resides, then using that path to find other application files (like game assets if your program is a game) is perfectly fine, since opening files is relative to the working directory, or, if provided, the root.
The following works as a quick and dirty solution, but note that it is far from being foolproof:
#include <iostream>
using namespace std ;
int main( int argc, char** argv)
{
cout << argv[0] << endl ;
return 0;
}
In case you need to handle unicode paths for Windows:
#include <Windows.h>
#include <iostream>
int wmain(int argc, wchar_t * argv[])
{
HMODULE this_process_handle = GetModuleHandle(NULL);
wchar_t this_process_path[MAX_PATH];
GetModuleFileNameW(NULL, this_process_path, sizeof(this_process_path));
std::wcout << "Unicode path of this app: " << this_process_path << std::endl;
return 0;
}
There are several answers recommending using GetModuleFileName on Windows. These answers have some shortcomings like:
The code should work for both UNICODE and ANSI versions
The path can be longer than MAX_PATH
GetModuleFileName function can fail and return 0
GetModuleFileName can return a relative executable name instead of a full name
GetModuleFileName can return a short path like C:\GIT-RE~1\TEST_G~1\test.exe
Let me provide an improved version, which takes into account the abovementioned points:
#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>
// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
// First we need to get a length of the full name string
const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
if (full_name_length == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
if (res == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// The full name has been successfully written to the buffer.
return std::basic_string<TCHAR>(full_name_buffer.get());
}
// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
// First we need to get a length of the long name string
const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
if (long_name_length == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
if (res == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// The long name has been successfully written to the buffer.
return std::basic_string<TCHAR>(long_name_buffer.get());
}
std::basic_string<TCHAR> get_current_executable_full_name()
{
DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that
// the path doesn't exceeds 260 characters
std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};
while (true) {
const auto bytes_written = GetModuleFileName(
NULL, path_buffer.get(), path_buffer_size);
const auto last_error = GetLastError();
if (bytes_written == 0) {
// GetModuleFileName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
if (last_error == ERROR_INSUFFICIENT_BUFFER) {
// There is not enough space in our buffer to fit the path.
// We need to increase the buffer and try again.
path_buffer_size *= 2;
path_buffer.reset(new TCHAR[path_buffer_size]);
continue;
}
// GetModuleFileName has successfully written the executable name to the buffer.
// Now we need to convert it to a full long name
std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
return get_long_name(full_name.c_str());
}
}
// Example of how this function can be used
int main()
{
#ifdef UNICODE
// If you use UNICODE version of WinApi
std::wstring exe_file_full_name = get_current_executable_full_name();
std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
// If you use ANSI version of WinApi
std::string exe_file_full_name = get_current_executable_full_name();
std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}
For Windows, you have the problem of how to strip the executable from the result of GetModuleFileName(). The Windows API call PathRemoveFileSpec() that Nate used for that purpose in his answer changed between Windows 8 and its predecessors. So how to remain compatible with both and safe? Luckily, there's C++17 (or Boost, if you're using an older compiler). I do this:
#include <windows.h>
#include <string>
#include <filesystem>
namespace fs = std::experimental::filesystem;
// We could use fs::path as return type, but if you're not aware of
// std::experimental::filesystem, you probably handle filenames
// as strings anyway in the remainder of your code. I'm on Japanese
// Windows, so wide chars are a must.
std::wstring getDirectoryWithCurrentExecutable()
{
int size = 256;
std::vector<wchar_t> charBuffer;
// Let's be safe, and find the right buffer size programmatically.
do {
size *= 2;
charBuffer.resize(size);
// Resize until filename fits. GetModuleFileNameW returns the
// number of characters written to the buffer, so if the
// return value is smaller than the size of the buffer, it was
// large enough.
} while (GetModuleFileNameW(NULL, charBuffer.data(), size) == size);
// Typically: c:/program files (x86)/something/foo/bar/exe/files/win64/baz.exe
// (Note that windows supports forward and backward slashes as path
// separators, so you have to be careful when searching through a path
// manually.)
// Let's extract the interesting part:
fs::path path(charBuffer.data()); // Contains the full path including .exe
return path.remove_filename() // Extract the directory ...
.w_str(); // ... and convert to a string.
}
SDL2 (https://www.libsdl.org/) library has two functions implemented across a wide spectrum of platforms:
SDL_GetBasePath
SDL_GetPrefPath
So if you don't want to reinvent the wheel... sadly, it means including the entire library, although it's got a quite permissive license and one could also just copy the code. Besides, it provides a lot of other cross-platform functionality.
I didn't read if my solution is already posted but on linux and osx you can read the 0 argument in your main function like this:
int main(int argument_count, char **argument_list) {
std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
std::size_t pos = currentWorkingDirectoryPath.rfind("/"); // position of "live" in str
currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);
In the first item of argument_list the name of the executable is integrated but removed by the code above.
Here my simple solution that works in both Windows and Linux, based on this solution and modified with this answer:
#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux
string replace(const string& s, const string& from, const string& to) {
string r = s;
int p = 0;
while((p=(int)r.find(from, p))!=string::npos) {
r.replace(p, from.length(), to);
p += (int)to.length();
}
return r;
}
string get_exe_path() { // returns path where executable is located
string path = "";
#if defined(_WIN32)
wchar_t wc[260] = {0};
GetModuleFileNameW(NULL, wc, 260);
wstring ws(wc);
transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
path = replace(path, "\\", "/");
#elif defined(__linux__)
char c[260];
int length = (int)readlink("/proc/self/exe", c, 260);
path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
return path.substr(0, path.rfind('/')+1);
}
This was my solution in Windows. It is called like this:
std::wstring sResult = GetPathOfEXE(64);
Where 64 is the minimum size you think the path will be. GetPathOfEXE calls itself recursively, doubling the size of the buffer each time until it gets a big enough buffer to get the whole path without truncation.
std::wstring GetPathOfEXE(DWORD dwSize)
{
WCHAR* pwcharFileNamePath;
DWORD dwLastError;
HRESULT hrError;
std::wstring wsResult;
DWORD dwCount;
pwcharFileNamePath = new WCHAR[dwSize];
dwCount = GetModuleFileNameW(
NULL,
pwcharFileNamePath,
dwSize
);
dwLastError = GetLastError();
if (ERROR_SUCCESS == dwLastError)
{
hrError = PathCchRemoveFileSpec(
pwcharFileNamePath,
dwCount
);
if (S_OK == hrError)
{
wsResult = pwcharFileNamePath;
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
return wsResult;
}
else if(S_FALSE == hrError)
{
wsResult = pwcharFileNamePath;
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
//there was nothing to truncate off the end of the path
//returning something better than nothing in this case for the user
return wsResult;
}
else
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
std::ostringstream oss;
oss << "could not get file name and path of executing process. error truncating file name off path. last error : " << hrError;
throw std::runtime_error(oss.str().c_str());
}
}
else if (ERROR_INSUFFICIENT_BUFFER == dwLastError)
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
return GetPathOfEXE(
dwSize * 2
);
}
else
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
std::ostringstream oss;
oss << "could not get file name and path of executing process. last error : " << dwLastError;
throw std::runtime_error(oss.str().c_str());
}
}
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
in Unix(including Linux) try 'which', in Windows try 'where'.
#include <stdio.h>
#define _UNIX
int main(int argc, char** argv)
{
char cmd[128];
char buf[128];
FILE* fp = NULL;
#if defined(_UNIX)
sprintf(cmd, "which %s > my.path", argv[0]);
#else
sprintf(cmd, "where %s > my.path", argv[0]);
#endif
system(cmd);
fp = fopen("my.path", "r");
fgets(buf, sizeof(buf), fp);
fclose(fp);
printf("full path: %s\n", buf);
unlink("my.path");
return 0;
}
As of C++17:
Make sure you include std filesystem.
#include <filesystem>
and now you can do this.
std::filesystem::current_path().string()
boost filesystem became part of the standard lib.
if you can't find it try to look under:
std::experimental::filesystem

Interprocess communication - Binary data/serialized objects [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I currently have two processes running on the same computer, a 'producer' and a Qt application tasked with displaying the data. These two need to exchange binary data (byte arrays, serialized objects). The data 'parcels' may range in size from a handful of bytes to tens of MBs.
What's a/the simple and elegant way to do this?
I thought about using boost::asio and Google Protocol Buffers, shared memory regions or just low-level sockets to achieve this, but I'm interested in learning about other solutions that I may have overlooked.
Performance is not absolutely critical but decent latency (<1 sec) and bandwidth (let's say, >5MB/sec) are necessary.
Thanks in advance!
If you don't like extra libraries then you can try the following:
#if defined _WIN32 || defined _WIN64
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include <cstdint>
typedef struct
{
#if defined _WIN32 || defined _WIN64
void* hFileMap;
#else
int hFileMap;
#endif
void* pData;
size_t size;
} MemoryMap;
void* CreateMemoryMap(MemoryMap* info, const char* MapName, unsigned int size)
{
#if defined _WIN32 || defined _WIN64
info->hFileMap = NULL;
info->pData = NULL;
info->size = 0;
if ((info->hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, MapName)) == NULL)
{
return NULL;
}
if ((info->pData = MapViewOfFile(info->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == NULL)
{
CloseHandle(info->hFileMap);
return NULL;
}
#else
info->hFileMap = NULL;
info->pData = NULL;
info->size = 0;
if ((info->hFileMap = open(MapName, O_RDWR | O_CREAT, 438)) == -1)
{
return NULL;
}
if ((info->pData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
{
close(hFileMap);
return NULL;
}
#endif
info->size = size;
return info->pData;
}
void* OpenMemoryMap(MemoryMap* info, const char* MapName, unsigned int size)
{
#if defined _WIN32 || defined _WIN64
info->hFileMap = NULL;
info->pData = NULL;
info->size = 0;
if ((info->hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, MapName)) == NULL)
{
return NULL;
}
if ((info->pData = MapViewOfFile(info->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == NULL)
{
CloseHandle(info->hFileMap);
return NULL;
}
#else
info->hFileMap = NULL;
info->pData = NULL;
info->size = 0;
if ((info->hFileMap = open(MapName, O_RDWR | O_CREAT, 438)) == -1)
{
return NULL;
}
if ((info->pData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
{
close(info->hFileMap);
return NULL;
}
#endif
info->size = size;
return info->pData;
}
void CloseMap(MemoryMap* data)
{
#if defined _WIN32 || defined _WIN64
UnmapViewOfFile(data->pData);
CloseHandle(data->hFileMap);
#else
munmap(data->pData, data->size);
close(data->hFileMap);
#endif
}
template<typename T>
class CAllocator
{
private:
size_t size;
void* data = nullptr;
public:
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T value_type;
CAllocator() {}
CAllocator(void* data_ptr, size_type max_size) noexcept : size(max_size), data(data_ptr) {};
template<typename U>
CAllocator(const CAllocator<U>& other) noexcept {};
CAllocator(const CAllocator &other) : size(other.size), data(other.data) {}
template<typename U>
struct rebind {typedef CAllocator<U> other;};
pointer allocate(size_type n, const void* hint = 0) {return static_cast<pointer>(data);}
void deallocate(void* ptr, size_type n) {}
size_type max_size() const {return size / sizeof(T);}
};
template <typename T, typename U>
inline bool operator == (const CAllocator<T>&, const CAllocator<U>&) {return true;}
template <typename T, typename U>
inline bool operator != (const CAllocator<T>& a, const CAllocator<U>& b) {return !(a == b);}
/** Test case **/
#include <vector>
#include <iostream>
#include <stdexcept>
int main()
{
/** Sender **/
MemoryMap data = {0};
if (!CreateMemoryMap(&data, "MapName", 1024))
{
if (!OpenMemoryMap(&data, "MapName", 1024))
{
throw std::runtime_error("Cannot map memory.");
}
}
std::vector<int, CAllocator<int>> shared_sender_vector(CAllocator<int>(data.pData, data.size));
shared_sender_vector.push_back(10);
for (int i = 0; i < 10; ++i)
{
shared_sender_vector.push_back(i + 1);
}
/** Receiver **/
MemoryMap data2 = {0};
if (!CreateMemoryMap(&data2, "MapName", 1024))
{
if (!OpenMemoryMap(&data2, "MapName", 1024))
{
throw std::runtime_error("Cannot map memory.");
}
}
int* offset = static_cast<int*>(data2.pData);
std::vector<int, CAllocator<int>> shared_receiver_vector(CAllocator<int>(++offset, data2.size));
shared_receiver_vector.reserve(*(--offset));
for (int i = 0; i < 10; ++i)
{
std::cout<<shared_receiver_vector[i]<<" ";
}
CloseMap(&data);
CloseMap(&data2);
}
It prints:
1 2 3 4 5 6 7 8 9 10
Can be used for strings and all sorts of containers that accepts allocators. They store their data directly in the shared-memory map. The allocator is the same as the one I wrote for:
Boost Pool experience requested. Is it useful as allocator with preallocation? so it can be reused for whatever you want..

Is there a windows concurrency_queue.h equivalent on linux?

Well I am tryng to have a queue which is concurrent, but concurrency_queue isn't standard C++ and it's for windows, linux doesn't have it. Is there anything for linux like this (with the same functions like in the windows equivalent?)?
Edit:
This is needed to port this windows code to linux:
#include <concurrent_queue.h>
#ifdef defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
#define SLEEP(x) { Sleep(x); }
#include <windows.h>
#include <process.h>
#define OS_WINDOWS
#define EXIT_THREAD() { _endthread(); }
#define START_THREAD(a, b) { _beginthread( a, 0, (void *)( b ) ); }
#else
#include <pthread.h>
#define sscanf_s sscanf
#define sprintf_s sprintf
#define EXIT_THREAD() { pthread_exit( NULL ); }
#define START_THREAD(a, b) { pthread_t thread;\
pthread_create( &thread, NULL, a, (void *)( b ) ); }
#endif
using namespace std;
using namespace Concurrency;
struct QuedData
{
int start;
int end;
int extraid;
AMX * script;
QuedData(){start = 0;end = 0;extraid = 0;script = NULL;}
QuedData(int start_,int end_,int extraid_, AMX * script_){start = start_;end = end_;extraid = extraid_;script = script_;}
};
struct PassData //thanks to DeadMG for improvements.
{
std::vector<cell> Paths;
int extraid;
AMX * script;
cell MoveCost;
PassData(){extraid = 0;script = NULL;MoveCost = 0;Paths.clear();}
template<typename Iterator> PassData(Iterator begin, Iterator end, int extraid_, cell MoveCost_, AMX * script_)
: Paths(begin, end)
{extraid = extraid_;MoveCost = MoveCost_;script = script_;}
~PassData(){Paths.clear();}
};
concurrent_queue <QuedData> QueueVector;
concurrent_queue <PassData> PassVector;
PassData LocalPass;
void PLUGIN_CALL
ProcessTick()
{
if(PassVector.try_pop(LocalPass))
{
amx_Push(LocalPass.script, LocalPass.MoveCost);
//blabla
}
}
static cell AMX_NATIVE_CALL n_CalculatePath( AMX* amx, cell* params )
{
QueueVector.push(QuedData(params[1],params[2],params[3],amx));
return 1;
}
bool PLUGIN_CALL Load( void **ppData )
{
START_THREAD( Thread::BackgroundCalculator, 0);
return true;
}
QuedData RecievedData;
vector <cell>tbcway;
cell tbccostx;
#ifdef OS_WINDOWS
void Thread::BackgroundCalculator( void *unused )
#else
void *Thread::BackgroundCalculator( void *unused )
#endif
{
while( true ){
if(QueueVector.try_pop(RecievedData)){
dgraph->findPath_r(xNode[RecievedData.start].NodeID ,xNode[RecievedData.end].NodeID,tbcway,tbccostx);
PassVector.push(PassData(tbcway.begin(),tbcway.end(),RecievedData.extraid,tbccostx,RecievedData.script));
}
SLEEP(5);
}
EXIT_THREAD();
}
The Visual C++ concurrent_queue is actually based on the Intel Threading Building Block Library
(If you open concurrent_queue.h header file in VC++ you will see an acknowledgement)
You can get the library from
http://threadingbuildingblocks.org/
The library will run on Linux as well.
I think threadpool does this or an unofficial Boost enhancement called lockfree and should by now be part of Boost::Atomics. I haven't use both but let us know if you have any luck.
I would suggest looking at https://github.com/romanek-adam/boost_locking_queue for the code and the article that goes with it at Implementing a Thread-Safe Queue using Condition Variables