Skip to content

Commit a6085b5

Browse files
authored
feat: os get last error and fix issue with get_var on windows (#18)
1 parent 1b2cddc commit a6085b5

File tree

2 files changed

+183
-140
lines changed

2 files changed

+183
-140
lines changed

share/cpp2b/cpp2b.cppm.tpl

Lines changed: 173 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
module;
22

33
#ifdef _MSC_VER
4-
# include <Windows.h>
4+
# include <Windows.h>
55
#else
6-
# include <stdlib.h>
7-
# include <unistd.h>
6+
# include <stdlib.h>
7+
# include <unistd.h>
88
#endif
99

1010
export module cpp2b;
@@ -13,9 +13,9 @@ import std;
1313
import std.compat;
1414

1515
#ifdef _MSC_VER
16-
extern char **_environ;
16+
extern char** _environ;
1717
#else
18-
extern "C" char **environ;
18+
extern "C" char** environ;
1919
#endif
2020

2121
export namespace cpp2b {
@@ -26,193 +26,227 @@ enum class compilation_type { debug, optimized, fast };
2626

2727
constexpr auto host_platform() -> platform {
2828
#if defined(_WIN32)
29-
return platform::windows;
29+
return platform::windows;
3030
#elif defined(__APPLE__)
31-
return platform::macos;
31+
return platform::macos;
3232
#elif defined(__linux__)
33-
return platform::linux;
33+
return platform::linux;
3434
#else
35-
# error unknown platform
35+
# error unknown platform
3636
#endif
3737
}
3838

3939
constexpr auto target_platform() -> platform {
40-
return host_platform();
40+
return host_platform();
4141
}
4242

4343
constexpr auto compiler() -> compiler_type {
4444
#if defined(_MSC_VER)
45-
return compiler_type::msvc;
45+
return compiler_type::msvc;
4646
#elif defined(__clang__)
47-
return compiler_type::clang;
47+
return compiler_type::clang;
4848
#elif defined(__GNUC__)
49-
return compiler_type::gcc;
49+
return compiler_type::gcc;
5050
#else
51-
# error unknown compiler
51+
# error unknown compiler
5252
#endif
5353
}
5454
} // namespace cpp2b
5555

5656
export namespace cpp2b::env {
5757
inline auto set_var(const std::string& name, const std::string& value) -> void {
5858
#if defined(_MSC_VER)
59-
SetEnvironmentVariableA(name.c_str(), value.c_str());
59+
SetEnvironmentVariableA(name.c_str(), value.c_str());
6060
#else
61-
setenv(name.c_str(), value.c_str(), 1);
61+
setenv(name.c_str(), value.c_str(), 1);
6262
#endif
6363
}
6464

6565
inline auto set_var(auto name, auto value) -> void {
66-
return cpp2b::env::set_var(std::string{name}, std::string{value});
66+
return cpp2b::env::set_var(std::string{name}, std::string{value});
6767
}
6868

6969
inline auto get_var(const std::string& name) -> std::optional<std::string> {
7070
#if defined(_MSC_VER)
71-
auto val = std::string{""};
72-
val.resize(GetEnvironmentVariableA(name.c_str(), val.data(), 1));
73-
74-
if(!val.empty()) {
75-
GetEnvironmentVariableA(name.c_str(), val.data(), val.size() + 1);
76-
return val;
77-
}
71+
DWORD val_len = GetEnvironmentVariableA(name.c_str(), nullptr, 0);
72+
if(val_len == 0) {
73+
return {};
74+
}
75+
76+
auto val = std::string(val_len - 1, '\0');
77+
GetEnvironmentVariableA(name.c_str(), val.data(), val_len);
78+
return val;
7879
#else
79-
auto val = std::getenv(name.c_str());
80-
if(val != nullptr) {
81-
return std::string{val};
82-
}
80+
auto val = std::getenv(name.c_str());
81+
if(val != nullptr) {
82+
return std::string{val};
83+
}
8384
#endif
8485

85-
return {};
86+
return {};
8687
}
8788

8889
inline auto get_var(auto name) -> std::optional<std::string> {
89-
return cpp2b::env::get_var(std::string{name});
90+
return cpp2b::env::get_var(std::string{name});
9091
}
9192

9293
class vars {
9394
public:
94-
struct entry {
95-
std::string_view name;
96-
std::string_view value;
97-
};
98-
99-
class iterator {
100-
public:
101-
using iterator_category = std::forward_iterator_tag;
102-
using value_type = entry;
103-
using difference_type = std::ptrdiff_t;
104-
using pointer = const value_type*;
105-
using reference = const value_type&;
106-
107-
iterator(char** env) : env_(env) {
108-
if (env_ && *env_) {
109-
update_entry();
110-
}
111-
}
112-
113-
reference operator*() const noexcept {
114-
return current_entry_;
115-
}
116-
117-
pointer operator->() const noexcept {
118-
return &current_entry_;
119-
}
120-
121-
iterator& operator++() noexcept {
122-
if (env_ && *env_) {
123-
++env_;
124-
if (*env_) {
125-
update_entry();
126-
} else {
127-
env_ = nullptr; // end of iteration
128-
}
129-
}
130-
return *this;
131-
}
132-
133-
iterator operator++(int) noexcept {
134-
iterator temp = *this;
135-
++(*this);
136-
return temp;
137-
}
138-
139-
friend bool operator==(const iterator& a, const iterator& b) noexcept {
140-
return a.env_ == b.env_;
141-
}
142-
143-
friend bool operator!=(const iterator& a, const iterator& b) noexcept {
144-
return !(a == b);
145-
}
146-
147-
private:
148-
void update_entry() noexcept {
149-
if (env_ && *env_) {
150-
std::string_view env_str(*env_);
151-
auto pos = env_str.find('=');
152-
if (pos != std::string_view::npos) {
153-
current_entry_.name = env_str.substr(0, pos);
154-
current_entry_.value = env_str.substr(pos + 1);
155-
}
156-
}
157-
}
158-
159-
char** env_ = nullptr;
160-
value_type current_entry_;
161-
};
162-
163-
iterator begin() const noexcept {
95+
struct entry {
96+
std::string_view name;
97+
std::string_view value;
98+
};
99+
100+
class iterator {
101+
public:
102+
using iterator_category = std::forward_iterator_tag;
103+
using value_type = entry;
104+
using difference_type = std::ptrdiff_t;
105+
using pointer = const value_type*;
106+
using reference = const value_type&;
107+
108+
iterator(char** env) : env_(env) {
109+
if(env_ && *env_) {
110+
update_entry();
111+
}
112+
}
113+
114+
reference operator*() const noexcept {
115+
return current_entry_;
116+
}
117+
118+
pointer operator->() const noexcept {
119+
return &current_entry_;
120+
}
121+
122+
iterator& operator++() noexcept {
123+
if(env_ && *env_) {
124+
++env_;
125+
if(*env_) {
126+
update_entry();
127+
} else {
128+
env_ = nullptr; // end of iteration
129+
}
130+
}
131+
return *this;
132+
}
133+
134+
iterator operator++(int) noexcept {
135+
iterator temp = *this;
136+
++(*this);
137+
return temp;
138+
}
139+
140+
friend bool operator==(const iterator& a, const iterator& b) noexcept {
141+
return a.env_ == b.env_;
142+
}
143+
144+
friend bool operator!=(const iterator& a, const iterator& b) noexcept {
145+
return !(a == b);
146+
}
147+
148+
private:
149+
void update_entry() noexcept {
150+
if(env_ && *env_) {
151+
std::string_view env_str(*env_);
152+
auto pos = env_str.find('=');
153+
if(pos != std::string_view::npos) {
154+
current_entry_.name = env_str.substr(0, pos);
155+
current_entry_.value = env_str.substr(pos + 1);
156+
}
157+
}
158+
}
159+
160+
char** env_ = nullptr;
161+
value_type current_entry_;
162+
};
163+
164+
iterator begin() const noexcept {
164165
#ifdef _MSC_VER
165-
return iterator(_environ);
166+
return iterator(_environ);
166167
#else
167-
return iterator(environ);
168+
return iterator(environ);
168169
#endif
169-
}
170+
}
170171

171-
iterator end() const noexcept {
172-
return iterator(nullptr);
173-
}
172+
iterator end() const noexcept {
173+
return iterator(nullptr);
174+
}
174175
};
175176

176177
std::filesystem::path executable_path() {
177-
static std::string executable_path_str;
178+
static std::string executable_path_str;
178179
179-
if(!executable_path_str.empty()) {
180-
return executable_path_str;
181-
}
180+
if(!executable_path_str.empty()) {
181+
return executable_path_str;
182+
}
182183

183-
auto size = 260;
184-
auto buffer = std::vector<char>(size);
184+
auto size = 260;
185+
auto buffer = std::vector<char>(size);
185186

186187
#if defined(_WIN32)
187-
for (;;) {
188-
DWORD len = GetModuleFileNameA(NULL, buffer.data(), size);
189-
if (len == 0) {
190-
executable_path_str = {};
191-
return executable_path_str;
192-
} else if (len < size - 1) {
193-
executable_path_str = std::string(buffer.data(), len);
194-
return executable_path_str;
195-
}
196-
197-
size += 260;
198-
buffer.resize(size);
199-
}
188+
for(;;) {
189+
DWORD len = GetModuleFileNameA(NULL, buffer.data(), size);
190+
if(len == 0) {
191+
executable_path_str = {};
192+
return executable_path_str;
193+
} else if(len < size - 1) {
194+
executable_path_str = std::string(buffer.data(), len);
195+
return executable_path_str;
196+
}
197+
198+
size += 260;
199+
buffer.resize(size);
200+
}
200201
#elif defined(__linux__)
201-
for (;;) {
202-
ssize_t len = readlink("/proc/self/exe", buffer.data(), size - 1);
203-
if (len < 0) {
204-
executable_path_str = {};
205-
return executable_path_str;
206-
} else if (len < size - 1) {
207-
executable_path_str = std::string(buffer.data(), len);
208-
return executable_path_str;
209-
}
210-
211-
size += 260;
212-
buffer.resize(size);
213-
}
202+
for(;;) {
203+
ssize_t len = readlink("/proc/self/exe", buffer.data(), size - 1);
204+
if(len < 0) {
205+
executable_path_str = {};
206+
return executable_path_str;
207+
} else if(len < size - 1) {
208+
executable_path_str = std::string(buffer.data(), len);
209+
return executable_path_str;
210+
}
211+
212+
size += 260;
213+
buffer.resize(size);
214+
}
214215
#else
215-
# error unhandled executable_path platform
216+
# error unhandled executable_path platform
216217
#endif
217218
}
219+
} // namespace cpp2b::env
220+
221+
export namespace cpp2b::os {
222+
auto get_last_error() -> std::string {
223+
#if defined(_WIN32)
224+
DWORD errorMessageID = ::GetLastError();
225+
if(errorMessageID == 0) {
226+
return {}; // No error
227+
}
228+
229+
LPSTR messageBuffer = nullptr;
230+
231+
size_t size = FormatMessageA(
232+
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
233+
FORMAT_MESSAGE_IGNORE_INSERTS,
234+
nullptr,
235+
errorMessageID,
236+
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
237+
(LPSTR)&messageBuffer,
238+
0,
239+
nullptr
240+
);
241+
242+
auto message = std::string{messageBuffer, size - 1};
243+
244+
LocalFree(messageBuffer);
245+
246+
return message;
247+
#else
248+
// TODO: other platforms
249+
return "";
250+
#endif
218251
}
252+
} // namespace cpp2b::os

0 commit comments

Comments
 (0)