From de7a01d32216ee16ac3bffdc86b7e30c2f4373ac Mon Sep 17 00:00:00 2001 From: Gregory Pakosz Date: Sun, 30 Apr 2017 17:36:55 +0200 Subject: [PATCH] added a C++ API --- _gnu-make/Makefile | 30 ++++++++++-- example/executable.cpp | 101 +++++++++++++++++++++++++++++++++++++++ example/library.cpp | 61 ++++++++++++++++++++++++ src/whereami++.cpp | 104 +++++++++++++++++++++++++++++++++++++++++ src/whereami++.h | 67 ++++++++++++++++++++++++++ src/whereami.c | 7 +++ src/whereami.h | 2 +- 7 files changed, 366 insertions(+), 6 deletions(-) create mode 100644 example/executable.cpp create mode 100644 example/library.cpp create mode 100644 src/whereami++.cpp create mode 100644 src/whereami++.h diff --git a/_gnu-make/Makefile b/_gnu-make/Makefile index cf5954b..2ce688c 100644 --- a/_gnu-make/Makefile +++ b/_gnu-make/Makefile @@ -84,28 +84,48 @@ endif .PHONY: build-executable build: build-executable -build-executable: $(bindir)/executable $(bindir)/executable-cpp +build-executable: $(bindir)/executable $(bindir)/executable-as-cxx $(bindir)/executable-as-cxx11 $(bindir)/executable-cxx $(bindir)/executable-cxx11 $(bindir)/executable: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c mkdir -p $(@D) $(CC) -I $(srcdir) $(CPPFLAGS) $(CFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ $(if $(postbuild),$(postbuild) $@) -$(bindir)/executable-cpp: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c +$(bindir)/executable-as-cxx: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c mkdir -p $(@D) - $(CXX) -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(CXX) -std=c++03 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/executable-as-cxx11: $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/executable.c + mkdir -p $(@D) + $(CXX) -std=c++11 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/executable-cxx: $(srcdir)/whereami++.cpp $(srcdir)/whereami++.h $(exampledir)/executable.cpp + mkdir -p $(@D) + $(CXX) -std=c++03 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/executable-cxx11: $(srcdir)/whereami++.cpp $(srcdir)/whereami++.h $(exampledir)/executable.cpp + mkdir -p $(@D) + $(CXX) -std=c++11 -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -o $@ $(if $(postbuild),$(postbuild) $@) .PHONY: build-library build: build-library -build-library: $(bindir)/library$(libsuffix) $(bindir)/library-cpp$(libsuffix) +build-library: $(bindir)/library$(libsuffix) $(bindir)/library-as-cxx$(libsuffix) $(bindir)/library-cxx$(libsuffix) $(bindir)/library$(libsuffix): $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/library.c mkdir -p $(@D) $(CC) -I $(srcdir) $(CPPFLAGS) $(CFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -shared -o $@ $(if $(postbuild),$(postbuild) $@) -$(bindir)/library-cpp$(libsuffix): $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/library.c +$(bindir)/library-as-cxx$(libsuffix): $(srcdir)/whereami.c $(srcdir)/whereami.h $(exampledir)/library.c + mkdir -p $(@D) + $(CXX) -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -shared -o $@ + $(if $(postbuild),$(postbuild) $@) + +$(bindir)/library-cxx$(libsuffix): $(srcdir)/whereami++.cpp $(srcdir)/whereami++.h $(exampledir)/library.cpp mkdir -p $(@D) $(CXX) -x c++ -I $(srcdir) $(CPPFLAGS) $(CXXFLAGS) $(filter-out %.h,$^) $(LDFLAGS) -shared -o $@ $(if $(postbuild),$(postbuild) $@) diff --git a/example/executable.cpp b/example/executable.cpp new file mode 100644 index 0000000..ff07707 --- /dev/null +++ b/example/executable.cpp @@ -0,0 +1,101 @@ +#include + +#include + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include + +#define RTLD_LAZY 1 +#define RTLD_NOW 2 +#define RTLD_GLOBAL 4 +#define RTLD_LOCAL 8 + +static void* dlopen(const char* fileName, int mode) +{ + wchar_t buffer[MAX_PATH]; + + if (MultiByteToWideChar(CP_UTF8, 0, fileName, -1, buffer, sizeof(buffer) / sizeof(*buffer))) + { + wchar_t buffer_[MAX_PATH]; + + GetFullPathNameW(buffer, sizeof(buffer_) / sizeof(*buffer_), buffer_, NULL); + + return (void*)LoadLibraryW(buffer_); + } + + return NULL; +} + +static int dlclose(void* handle) +{ + return FreeLibrary((HMODULE)handle) ? 0 : -1; +} + +static const char* dlerror(void) +{ + DWORD error; + + error = GetLastError(); + + if (error) + { + static char message[1024]; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), message, sizeof(message), NULL); + + return message; + } + + return "no error"; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#else + +#include + +#endif + +int main(int argc, char** argv) +{ + std::cout << "executable path: " << whereami::getExecutablePath() << std::endl; + whereami::whereami_path_t executablePath = whereami::getExecutablePath(); + std::cout << " dirname: " << executablePath.dirname() << std::endl; + std::cout << " basename: " << executablePath.basename() << std::endl; + + std::cout << "module path: " << whereami::getModulePath() << std::endl; + whereami::whereami_path_t modulePath = whereami::getModulePath(); + std::cout << " dirname: " << modulePath.dirname() << std::endl; + std::cout << " basename: " << modulePath.basename() << std::endl; + + for (int i = 1; i < argc; ++i) + { + std::string prefix = "--load-library="; + std::string arg = std::string(argv[i]); + + if (arg.compare(0, prefix.length(), prefix) == 0) + { + std::string name = arg.substr(prefix.length()); + void* handle; + + std::cout << std::endl; + + handle = dlopen(name.c_str(), RTLD_NOW); + if (!handle) + std::cout << "failed to load library: " << dlerror() << std::endl; + + if (handle) + dlclose(handle); + } + } + + return 0; +} diff --git a/example/library.cpp b/example/library.cpp new file mode 100644 index 0000000..ce0c253 --- /dev/null +++ b/example/library.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#if defined(__GNUC__) && !defined(_WIN32) +__attribute__((constructor)) +#endif +static void load() +{ + std::cout << "library loaded" << std::endl; + + std::cout << "executable path: " << whereami::getExecutablePath() << std::endl; + whereami::whereami_path_t executablePath = whereami::getExecutablePath(); + std::cout << " dirname: " << executablePath.dirname() << std::endl; + std::cout << " basename: " << executablePath.basename() << std::endl; + + std::cout << "module path: " << whereami::getModulePath() << std::endl; + whereami::whereami_path_t modulePath = whereami::getModulePath(); + std::cout << " dirname: " << modulePath.dirname() << std::endl; + std::cout << " basename: " << modulePath.basename() << std::endl; +} + +#if defined(__GNUC__) && !defined(_WIN32) +__attribute__((destructor)) +#endif +static void unload() +{ + std::cout << "library unloaded" << std::endl; +} + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + load(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + unload(); + break; + } + return TRUE; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif diff --git a/src/whereami++.cpp b/src/whereami++.cpp new file mode 100644 index 0000000..8334ed9 --- /dev/null +++ b/src/whereami++.cpp @@ -0,0 +1,104 @@ +// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +// in case you want to #include "whereami++.cpp" in a larger compilation unit +#if !defined(WHEREAMIPP_H) +#include +#endif + +namespace whereami { + +namespace { + #define WHEREAMI_H + #define WAI_FUNCSPEC + #define WAI_PREFIX(function) function + #include "whereami.c" +} + + whereami_string_t whereami_path_t::dirname() const + { + return path.substr(0, dirname_length); + } + + whereami_string_t whereami_path_t::basename() const + { + return path.substr(dirname_length + 1); + } + +#if defined(WHEREAMI_CXX11) + whereami_path_t::operator whereami_string_t() && + { + return std::move(path); + } + + whereami_path_t::operator whereami_string_t() const & + { + return path; + } +#else + whereami_path_t::operator const whereami_string_t&() const + { + return path; + } +#endif + +#if defined(WHEREAMI_CXX11) + whereami_path_t::whereami_path_t(whereami_string_t&& path, int dirname_length) noexcept + : path(std::move(path)), dirname_length(dirname_length) + { + } +#else + whereami_path_t::whereami_path_t(whereami_string_t& path, int dirname_length) + : path(path), dirname_length(dirname_length) + { + } +#endif + +#if !defined(WHEREAMI_DISABLE_OSTREAM) + std::ostream& operator<<(std::ostream& os, const whereami_path_t& path) + { + return os << path.path; + } + +#endif + WAI_FUNCSPEC + whereami_path_t getExecutablePath() + { + whereami_string_t path; + int dirname_length = -1; + + int length = getExecutablePath(0, 0, 0); + if (length != -1) + { + path.resize(length); + getExecutablePath(&path[0], length, &dirname_length); + } + +#if defined(WHEREAMI_CXX11) + return whereami_path_t(std::move(path), dirname_length); +#else + return whereami_path_t(path, dirname_length); +#endif + } + + WAI_FUNCSPEC + whereami_path_t getModulePath() + { + whereami_string_t path; + int dirname_length = -1; + + int length = getModulePath(0, 0, 0); + if (length != -1) + { + path.resize(length); + getModulePath(&path[0], length, &dirname_length); + } + +#if defined(WHEREAMI_CXX11) + return whereami_path_t(std::move(path), dirname_length); +#else + return whereami_path_t(path, dirname_length); +#endif + } + +} // namespace whereami diff --git a/src/whereami++.h b/src/whereami++.h new file mode 100644 index 0000000..928a19b --- /dev/null +++ b/src/whereami++.h @@ -0,0 +1,67 @@ +// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +#ifndef WHEREAMIPP_H +#define WHEREAMIPP_H + +#if !defined(WHEREAMI_STRING_T) +#include +typedef std::string whereami_string_t; +#else +typedef WHEREAMI_STRING_T whereami_string_t; +#endif +#if !defined(WHEREAMI_DISABLE_OSTREAM) +#include +#endif + +#if (defined (__cplusplus) && (__cplusplus > 199711L)) || (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706)) + #define WHEREAMI_CXX11 +#endif + +namespace whereami { + + class whereami_path_t + { + public: +#if defined(WHEREAMI_CXX11) + operator whereami_string_t() &&; + operator whereami_string_t() const &; +#else + operator const whereami_string_t&() const; +#endif + whereami_string_t dirname() const; + whereami_string_t basename() const; + + private: + whereami_path_t(); + +#if defined(WHEREAMI_CXX11) + whereami_path_t(whereami_string_t&& path, int dirname_length) noexcept; +#else + whereami_path_t(whereami_string_t& path, int dirname_length); +#endif + + friend whereami_path_t getExecutablePath(); + friend whereami_path_t getModulePath(); +#if !defined(WHEREAMI_DISABLE_OSTREAM) + friend std::ostream& operator<<(std::ostream& os, const whereami_path_t& path); +#endif + + whereami_string_t path; + int dirname_length; + }; + + /** + * Returns the path to the current executable. + */ + whereami_path_t getExecutablePath(); + + /** + * Returns the path to the current module. + */ + whereami_path_t getModulePath(); + +} // namespace whereami + +#endif // #ifndef WHEREAMIPP_H + diff --git a/src/whereami.c b/src/whereami.c index 6ebd5a2..3f2939d 100644 --- a/src/whereami.c +++ b/src/whereami.c @@ -10,6 +10,13 @@ extern "C" { #endif +#ifndef WAI_FUNCSPEC + #define WAI_FUNCSPEC +#endif +#ifndef WAI_PREFIX +#define WAI_PREFIX(function) wai_##function +#endif + #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) #include #endif diff --git a/src/whereami.h b/src/whereami.h index 6c81af8..86b129a 100644 --- a/src/whereami.h +++ b/src/whereami.h @@ -38,7 +38,7 @@ WAI_FUNCSPEC int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); /** - * Returns the path to the current module + * Returns the path to the current module. * * Usage: * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve