Created
April 3, 2016 20:17
-
-
Save czipperz/ca36868273d193b48ec7edcc84051e6e to your computer and use it in GitHub Desktop.
Define basic manual variant class.
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
#include <string> | |
#include <exception> | |
#if __cplusplus < 201103L | |
// for c++98 compiler support | |
#define noexcept throw () | |
#endif | |
#if __cplusplus < 201103L | |
#define EQUALS_DELETE | |
#else | |
#define EQUALS_DELETE = delete | |
#endif | |
class cast_exception : public std::exception { | |
std::string message; | |
public: | |
cast_exception(std::string message) | |
: message(std::string("Invalid cast to type `") + message + | |
"`.") {} | |
const char* what() const noexcept { | |
return message.c_str(); | |
} | |
// for c++98 support you need this explicitly | |
~cast_exception() noexcept {} | |
}; | |
class Cell { | |
char _storage[sizeof(double) > sizeof(std::string) | |
? sizeof(double) | |
: sizeof(std::string)]; | |
/// 0 -> CellStr, 1 -> CellDbl, 2 -> None | |
size_t _which; | |
friend Cell CellStr(const std::string&); | |
friend Cell CellDbl(double); | |
friend Cell CellNone(); | |
Cell() {} | |
void destroy() { | |
if (_which == 0) { | |
// required! | |
using std::string; | |
unsafe_as_string().~string(); | |
} | |
// double and nothing don't have destructors | |
} | |
public: | |
~Cell() { destroy(); } | |
Cell(const Cell& res) { | |
if (&res == this) { | |
return; | |
} | |
destroy(); | |
switch (res._which) { | |
case 0: | |
new (_storage) std::string(res.unsafe_as_string()); | |
break; | |
case 1: | |
new (_storage) double(res.unsafe_as_double()); | |
break; | |
case 2: | |
break; | |
} | |
_which = res._which; | |
} | |
const std::string& unsafe_as_string() const { | |
return *reinterpret_cast<const std::string*>(&_storage); | |
} | |
const double& unsafe_as_double() const { | |
return *reinterpret_cast<const double*>(&_storage); | |
} | |
std::string& unsafe_as_string() { | |
return *reinterpret_cast<std::string*>(&_storage); | |
} | |
double& unsafe_as_double() { | |
return *reinterpret_cast<double*>(&_storage); | |
} | |
bool is_string() const { | |
return _which == 0; | |
} | |
bool is_double() const { | |
return _which == 1; | |
} | |
bool is_none() const { | |
return _which == 2; | |
} | |
const std::string& as_string() const { | |
if (is_string()) { | |
return unsafe_as_string(); | |
} else { | |
throw cast_exception("string"); | |
} | |
} | |
const double& as_double() const { | |
if (is_double()) { | |
return unsafe_as_double(); | |
} else { | |
throw cast_exception("double"); | |
} | |
} | |
std::string& as_string() { | |
if (is_string()) { | |
return unsafe_as_string(); | |
} else { | |
throw cast_exception("string"); | |
} | |
} | |
double& as_double() { | |
if (is_double()) { | |
return unsafe_as_double(); | |
} else { | |
throw cast_exception("double"); | |
} | |
} | |
template <typename Visitor> | |
typename Visitor::result_type | |
apply_visitor(Visitor& visitor) const { | |
switch (_which) { | |
case 0: | |
return visitor(as_string()); | |
break; | |
case 1: | |
return visitor(as_double()); | |
break; | |
case 2: | |
return visitor(); | |
break; | |
default: | |
std::terminate(); | |
} | |
} | |
template <typename Visitor> | |
typename Visitor::result_type | |
apply_visitor(Visitor& visitor) { | |
switch (_which) { | |
case 0: | |
return visitor(as_string()); | |
break; | |
case 1: | |
return visitor(as_double()); | |
break; | |
case 2: | |
return visitor(); | |
break; | |
default: | |
std::terminate(); | |
} | |
} | |
}; | |
template <typename Visitor> | |
class apply_visitor_delayed_t { | |
Visitor& visitor; | |
apply_visitor_delayed_t& operator=( | |
const apply_visitor_delayed_t&) EQUALS_DELETE; | |
public: | |
apply_visitor_delayed_t(Visitor& v) | |
: visitor(v) {} | |
typename Visitor::result_type | |
operator()(const Cell& c) { | |
c.apply_visitor(visitor); | |
} | |
typename Visitor::result_type | |
operator()(Cell& c) { | |
c.apply_visitor(visitor); | |
} | |
}; | |
template <typename Visitor> | |
apply_visitor_delayed_t<Visitor> | |
apply_visitor_delayed(Visitor& visitor) { | |
return apply_visitor_delayed_t<Visitor>(visitor); | |
} | |
Cell CellStr(const std::string& str) { | |
Cell cell; | |
new (cell._storage) std::string(str); | |
cell._which = 0; | |
return cell; | |
} | |
Cell CellDbl(double d) { | |
Cell cell; | |
new (cell._storage) double(d); | |
cell._which = 1; | |
return cell; | |
} | |
Cell CellNone() { | |
Cell cell; | |
cell._which = 2; | |
return cell; | |
} | |
///////////////////// | |
////// TESTS ////// | |
///////////////////// | |
struct V { | |
typedef int result_type; | |
result_type operator()() { | |
return 6; | |
} | |
result_type operator()(double) { | |
return 5; | |
} | |
result_type operator()(std::string) { | |
return 4; | |
} | |
}; | |
#include <assert.h> // can remove | |
int main() { | |
V visitor; | |
Cell n = CellNone(); | |
assert(n.is_none()); | |
assert(n.apply_visitor(visitor) == 6); | |
Cell d = CellDbl(3); | |
assert(d.is_double()); | |
assert(d.unsafe_as_double() == 3); | |
assert(d.apply_visitor(visitor) == 5); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment