Skip to content

Instantly share code, notes, and snippets.

@czipperz
Created April 3, 2016 20:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save czipperz/ca36868273d193b48ec7edcc84051e6e to your computer and use it in GitHub Desktop.
Save czipperz/ca36868273d193b48ec7edcc84051e6e to your computer and use it in GitHub Desktop.
Define basic manual variant class.
#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