#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
#include <fstream>
#include <chrono>
#include <ctime>
#if defined(_WIN32)
# define OS_PLATFORM Windows
#elif defined(DARWIN)
# define OS_PLATFORM macOS
#else
# define OS_PLATFORM Linux
#endif
#ifndef _MSC_VER
namespace std
{
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
}
inline int fopen_s(FILE** a_file, const char* a_fileName, const char* a_mode)
{
*a_file = fopen(a_fileName, a_mode);
return (*a_file != nullptr);
}
#endif
static std::string get_date_string(const char* fmtStr)
{
char some_buffer[64];
auto t = std::chrono::system_clock::now();
auto as_time_t = std::chrono::system_clock::to_time_t(t);
struct tm tm;
//if (::gmtime_r(&as_time_t, &tm))
if (::localtime_r(&as_time_t, &tm))
// if (std::strftime(some_buffer, sizeof(some_buffer), "%F", &tm))
if (std::strftime(some_buffer, sizeof(some_buffer), fmtStr, &tm))
return std::string{some_buffer};
throw std::runtime_error("Failed to get current date as string");
}
//! Reads in file contents of filename to dat
inline bool slurp(const char* filename, std::vector<uint8_t>& dat)
{
std::ifstream file(filename, std::ifstream::ate | std::ifstream::binary);
if (!file)
return false;
dat.resize(file.tellg());
dat.assign((std::istreambuf_iterator<char>(file.seekg(0, std::ios::beg))), std::istreambuf_iterator<char>());
return true;
}
inline static std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
inline static std::string join(const std::vector<std::string>& words, std::string delim)
{
std::string str;
if (!words.empty())
{
for (const auto& word : words)
str += word + delim;
// remove trailing delim
str.replace(str.rfind(delim), delim.length(), "");
}
return str;
}
inline static std::string trim(std::string str)
{
str.erase(0, str.find_first_not_of(" \t\r\n"));
str.erase(str.find_last_not_of(" \t\r\n") + 1);
return str;
}
inline static bool endsWith(const std::string& first, const std::string& other)
{
return (other.size() <= first.size()) && other == (first.substr(first.size() - other.size()));
}
inline static std::string str2lower(const std::string& str)
{
std::string ret = str;
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
return ret;
}
// TODO: perhaps rename this function, it converts the string "#010203" to the int 0x010203
inline static unsigned int str2col(std::string str)
{
if (str[0] == '#')
str = &str[1];
//return std::stoul(str, nullptr, 16);
return strtoul(str.c_str(), nullptr, 16);
}
float static inline str2float(const std::string& str)
{
return (float)atof(str.c_str());
}
// TODO: this doesn't work for any T, should work for int, perhaps need to add an std::enable_if
// the oss version appears to be broken for uint8_t. I think it doesn't treat it as an integer type
// but instead as a char type and does something different.
// The hexChars version might be better
template<typename T>
inline static std::string int2hex(const T& value)
{
std::string ret;
char hexChars[17] = "0123456789ABCDEF";
uint64_t val = (uint64_t)(value);
for (int i = 0; i < sizeof(T)*2; i++)
{
ret = hexChars[val&0xF] + ret;
val >>= 4;
}
return ret;
// This looks good: https://gist.github.com/miguelmota/4fc9b46cf21111af5fa613555c14de92
/*
std::ostringstream oss;
oss << std::setfill('0') << std::setw(sizeof(T)*2) << std::hex << value;
return oss.str();
*/
}
// https://stackoverflow.com/a/38885161 - this is best solution from performance point of view
template<template <typename...> class MAP, class KEY, class VALUE>
inline static std::vector<KEY> map2keys(const MAP<KEY, VALUE>& map)
{
std::vector<KEY> result;
result.reserve(map.size());
for (const auto& it : map)
result.emplace_back(it.first);
return result;
}
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
#define GENERATE_ENUM_SERIALIZATION_FUNCTIONS(EnumType, lastVal, defaultVal, ...) \
const char* EnumType##Strings[] = { __VA_ARGS__ }; \
static_assert((lastVal + 1) == ARRAY_SIZE(EnumType##Strings), "Please update the serialization for " #EnumType); \
EnumType stringTo##EnumType(const std::string& str) \
{ \
for (int i = 0; i < ARRAY_SIZE(EnumType##Strings); ++i) \
if (str == EnumType##Strings[i]) \
return (EnumType)i; \
return defaultVal; \
} \
std::string stringFrom##EnumType(EnumType e) \
{ \
if ((int)e >= 0 && (int)e < ARRAY_SIZE(EnumType##Strings)) \
return EnumType##Strings[(int)e]; \
return ""; \
}
static float stringToFloat(const std::string& str)
{
return (float)atof(str.c_str());
}
static std::string stringFromFloat(float val)
{
std::string str = std::to_string(val);
str = str.erase(str.find_last_not_of('0') + 1, std::string::npos);
str = str.erase(str.find_last_not_of('.') + 1, std::string::npos);
return str;
//return std::to_string(val);
}
static int stringToInt(const std::string& str)
{
return (int)atoi(str.c_str());
}
static std::string stringFromInt(int val)
{
return std::to_string(val);
}
static std::string stringToString(const std::string& str)
{
return str;
}
static std::string stringFromString(const std::string& str)
{
return str;
}
static int stringToColor(const std::string& str)
{
return (int)strtol(str.c_str() + 1, (char **)NULL, 16);
}
static std::string stringFromColor(int col)
{
return "#" + int2hex(col);
}