Skip to content

Instantly share code, notes, and snippets.

@BriFuture
Created June 22, 2020 15:13
Show Gist options
  • Save BriFuture/259844ddcbfe6891ab9207a23a0a065f to your computer and use it in GitHub Desktop.
Save BriFuture/259844ddcbfe6891ab9207a23a0a065f to your computer and use it in GitHub Desktop.
MSVC can not automatically convert value type
#pragma once
#include <type_traits>
#include <variant>
#include <string>
#include <vector>
#include <map>
#include <optional>
namespace gpds
{
class container;
template<class ...Ts> struct overload : Ts... { using Ts::operator()...; };
template<class ...Ts> overload(Ts...) -> overload<Ts...>;
template<typename T, typename ...Ts>
[[nodiscard]]
static constexpr bool contains()
{
return std::disjunction_v<std::is_same<T, Ts> ...>;
}
template<typename T>
[[nodiscard]]
static constexpr bool is_valid_type()
{
return contains<T, bool, int, double, std::string, gContainer>;
}
class value
{
public:
std::string comment;
value() = default;
value(const value& other);
value(value&& other);
virtual ~value() noexcept;
template<class T,
typename std::enable_if<not std::is_class<T>::value, T>::type* = nullptr>
value(const T& value)
{
set<T>(value);
}
template<class T,
typename std::enable_if<std::is_class<T>::value, T>::type* = nullptr>
value(T&& value)
{
set<T>(std::move(value));
}
template<typename T>
[[nodiscard]]
constexpr bool is_type() const noexcept
{
return std::holds_alternative<T>(m_value);
}
[[nodiscard]] constexpr bool is_empty() const
{
return m_value.valueless_by_exception();
}
[[nodiscard]] constexpr const char* type_string() const
{
if (std::holds_alternative<container*>(m_value)) {
return "nested";
}
return std::visit(overload{
[](const bool&) { return "bool"; },
[](const int&) { return "int"; },
[](const double&) { return "double"; },
[](const std::string&) { return "string"; }
}, m_value);
return "n/a";
}
void from_string(std::string&& string);
[[nodiscard]] std::string to_string() const;
template<typename T>
void set(const T& value)
{
m_value = value;
}
template<typename T>
void set(T&& value)
{
m_value = std::move(value);
}
template<typename T = std::string>
void set(const char* string)
{
m_value = std::string(string);
}
template<typename T = container&>
void set(const container& container)
{
free_container_memory();
allocate_container_memory(container);
}
template<typename T = container&&>
void set(container&& container)
{
free_container_memory();
allocate_container_memory(std::move(container));
}
template<typename T>
[[nodiscard]]
constexpr T get() const
{
return std::get<T>(m_value);
}
value& set_comment(const std::string& comment);
value& set_comment(std::string&& comment);
private:
std::variant<
bool,
int,
double,
std::string,
container *
> m_value;
void allocate_container_memory(const container& container);
void allocate_container_memory(container&& container);
void free_container_memory();
};
}
#include <stdexcept>
#include <stdexcept>
#include <cctype>
using namespace gpds;
template<typename T>
static std::string value_to_string(const T& value)
{
if constexpr (std::is_same<T, bool>::value) {
return value ? "true" : "false";
}
else if constexpr (std::is_same<T, int>::value) {
return std::to_string(value);
}
else if constexpr (std::is_same<T, double>::value) {
std::string str = std::to_string(value);
// Remove trailing zeros
str.erase(str.find_last_not_of('0') + 1, std::string::npos);
return str;
}
else if constexpr (std::is_same<T, std::string>::value) {
return value;
}
else if constexpr (is_c_str<T>::value) {
return std::string(value);
}
return {};
}
value::value(const value& other) :
comment(other.comment),
m_value(other.m_value)
{
if (std::holds_alternative<container*>(m_value)) {
allocate_container_memory(*std::get<container*>(m_value));
}
}
value::value(value&& other) :
comment(std::move(other.comment)),
m_value(std::move(other.m_value))
{
other.m_value = nullptr;
}
value::~value() noexcept
{
// Ensure that we won't throw
// GPDS_ASSERT(not m_value.valueless_by_exception());
free_container_memory();
}
void value::from_string(std::string&& string)
{
// Is it a boolean 'true' value?
if (string == "true") {
set(true);
return;
}
// Is it a boolean 'false' value?
if (string == "false") {
set(false);
return;
}
// Is it an integer?
{
// Ensure that this is an integer
bool isInteger = true;
for (std::string::const_iterator it = string.cbegin(); it != string.cend(); ++it) {
// Make sure that this is a digit
if (not std::isdigit(static_cast<int>( *it ))) {
isInteger = false;
}
// Check for minus sign
if (it == string.cbegin() and !isInteger and *it == '-') {
isInteger = true;
}
if (not isInteger) {
break;
}
}
if (isInteger) {
try {
int i = std::stoi(string);
set(i);
return;
} catch (const std::invalid_argument& e) {
(void) e;
// Nothing to do here. Fall through.
}
}
}
// Is it a double?
{
// Ensure that this is a double
bool isDouble = true;
bool foundPoint = false;
for (std::string::const_iterator it = string.cbegin(); it != string.cend(); ++it) {
// Make sure that this is a digit
if (not std::isdigit(static_cast<int>( *it ))) {
// Check if its a decimal point
if (!foundPoint and *it == '.') {
isDouble = true;
foundPoint = true;
} else {
isDouble = false;
}
}
// Check for minus sign
if (it == string.cbegin() and !isDouble and *it == '-') {
isDouble = true;
}
if (not isDouble) {
break;
}
}
if (isDouble) {
try {
double d = std::stod(string);
set(d);
return;
} catch (const std::invalid_argument& e) {
(void) e;
// Nothing to do here. Fall through.
}
}
}
// Lets just assume it's a string :>
{
set(std::move(string));
return;
}
}
std::string value::to_string() const
{
if (is_type<bool>()) {
return value_to_string(get<bool>());
}
else if (is_type<int>()) {
return value_to_string(get<int>());
}
else if (is_type<double>()) {
return value_to_string(get<double>());
}
else if (is_type<std::string>()) {
return value_to_string(get<std::string>());
}
return {};
}
value& value::set_comment(const std::string& comment)
{
this->comment = comment;
return *this;
}
value& value::set_comment(std::string&& comment)
{
this->comment = std::move(comment);
return *this;
}
void value::free_container_memory()
{
// Containers need to be cleaned up
if (std::holds_alternative<container*>(m_value)) {
delete std::get<container*>(m_value);
m_value = nullptr;
}
}
void value::allocate_container_memory(const container& container)
{
// m_value = new gpds::container(container);
}
void value::allocate_container_memory(container&& container)
{
// m_value = new gpds::container(std::move(container));
}
#include <map>
std::multimap<std::string, value> values;
template<class T>
value& add_value(const std::string& key, T&& value)
{
auto it = values.emplace(std::make_pair(key, std::forward<T>(value)));
return it->second;
}
int main() {
add_value("test", value()); // able to compile
// add_value("test", 1); // fail to compile
// add_value("test", 1.0); // fail to compile
// add_value("test", true); // fail to compile
return 0;
}
@BriFuture
Copy link
Author

codes are from GPDS

@BriFuture
Copy link
Author

Another example here compiles OK with g++: https://coliru.stacked-crooked.com/a/84ff2a772b7f46e1
But failed with MSVC.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment