Created
June 22, 2020 15:13
-
-
Save BriFuture/259844ddcbfe6891ab9207a23a0a065f to your computer and use it in GitHub Desktop.
MSVC can not automatically convert value type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
Another example here compiles OK with g++: https://coliru.stacked-crooked.com/a/84ff2a772b7f46e1
But failed with MSVC.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
codes are from GPDS