Last active
June 13, 2017 14:06
-
-
Save yudi-matsuzake/1ae4dcdad9716122bf19a3a6d8d20d18 to your computer and use it in GitHub Desktop.
This class implement a generic placeholder for any type in C++ using smart pointers
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
/** @file any.hpp | |
* This class implement a generic placeholder for any type. | |
* Inspired by the awesome article "Valued Convertions" by | |
* Kevlin Henney. | |
* http://www.two-sdg.demon.co.uk/curbralan/papers/ValuedConversions.pdf | |
*/ | |
#pragma once | |
#include <cstdlib> | |
#include <string> | |
#include <memory> | |
#include <utility> | |
#include <typeinfo> | |
#include <stdexcept> | |
#include <cxxabi.h> | |
#define ANY_DEMANGLE(X) abi::__cxa_demangle((X), 0, 0, NULL) | |
/** Any type class. Placeholder for any type like boost::any. | |
* This class uses only smart pointers for handling with | |
* placeholders. | |
*/ | |
class any { | |
public: | |
/** any::exception is a generic exception that | |
* any can throw. | |
*/ | |
class exception : public std::runtime_error{ | |
public: | |
explicit exception (const std::string& what_arg) | |
: std::runtime_error(what_arg) | |
{} | |
explicit exception (const char* what_arg) | |
: std::runtime_error(what_arg) | |
{} | |
}; | |
/** This can throw when the user of the class | |
* try to convert the placeholder to a different | |
* type. | |
* | |
* e.g. | |
* any a = 1; | |
* double d = a.get<double>(); // this will throw | |
* | |
* @see get | |
*/ | |
class invalid_type_convertion : public exception { | |
public: | |
explicit invalid_type_convertion ( | |
const std::type_info& a, | |
const std::type_info& b) | |
: exception(to_str(a, b)), | |
type_from(ANY_DEMANGLE(a.name()), std::free), | |
type_to(ANY_DEMANGLE(b.name()), std::free) | |
{} | |
std::unique_ptr<char, decltype(std::free)*> type_from; | |
std::unique_ptr<char, decltype(std::free)*> type_to; | |
private: | |
std::string to_str( | |
const std::type_info& a, | |
const std::type_info& b) | |
{ | |
std::unique_ptr<char, decltype(std::free)*> | |
astr(ANY_DEMANGLE(a.name()), std::free); | |
std::unique_ptr<char, decltype(std::free)*> | |
bstr(ANY_DEMANGLE(b.name()), std::free); | |
return std::string("Invalid type convertion from ") | |
+ std::string(astr.get()) | |
+ std::string(" to ") | |
+ std::string(bstr.get()) | |
+ std::string("."); | |
} | |
}; | |
any() : content(nullptr) | |
{} | |
any(const any& other) | |
: content(other.content ? other.content->clone() : nullptr) | |
{} | |
template<typename T> | |
any(const T& value) | |
: content(new holder<T>(value)) | |
{} | |
~any() | |
{} | |
const std::type_info& type_info() const | |
{ | |
return content ? content->type_info() : typeid(void); | |
} | |
any& operator=(const any& other) | |
{ | |
std::shared_ptr<placeholder> newcontent = other.content->clone(); | |
this->content = std::move(newcontent); | |
return *this; | |
} | |
template <typename T> | |
any& operator=(const T& value) | |
{ | |
return *this = any(value); | |
} | |
template <typename T> | |
const std::shared_ptr<T> to_ptr() const | |
{ | |
if(content.get() == nullptr) | |
return std::shared_ptr<T>(nullptr); | |
return (type_info() == typeid(T)) | |
? std::dynamic_pointer_cast<holder<T>>(content)->held | |
: nullptr; | |
} | |
/** Gets the value of the any type. | |
* | |
* @tparam T type of the placeoholder | |
* | |
* @return The object of type T | |
* | |
* @throw any::exception if placeholder is null | |
* | |
* @see any::exeception | |
*/ | |
template<typename T> | |
T& get() | |
{ | |
if(content.get() == nullptr) | |
throw exception("Placeholder is null"); | |
if(typeid(T) != content->type_info()){ | |
throw invalid_type_convertion( | |
content->type_info(), | |
typeid(T)); | |
} | |
return std::dynamic_pointer_cast<holder<T>>(content)->held; | |
} | |
private: | |
/** Abstract class that provides the hability to | |
* hold a placeholder of any type | |
*/ | |
class placeholder { | |
public: | |
virtual ~placeholder(){} | |
virtual const std::type_info& type_info() const = 0; | |
virtual std::shared_ptr<placeholder> clone() const = 0; | |
}; | |
/** Implements placeholder for any type | |
*/ | |
template <typename T> | |
class holder : public placeholder { | |
public: | |
holder(const T& value) : held(value) | |
{} | |
virtual const std::type_info& type_info() const | |
{ | |
return typeid(T); | |
} | |
virtual std::shared_ptr<placeholder> clone() const | |
{ | |
return std::shared_ptr<placeholder>(new holder(held)); | |
} | |
T held; | |
}; | |
std::shared_ptr<placeholder> content; | |
}; |
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 <iostream> | |
#include "any.hpp" | |
using namespace std; | |
class person { | |
public: | |
std::string name; | |
int age; | |
person(const std::string& name, int age) | |
: name(name), | |
age(age) | |
{} | |
friend std::ostream& operator<<(std::ostream& o, person& p) | |
{ | |
o << "[" << p.name << ", " << p.age << "]"; | |
return o; | |
} | |
}; | |
int main() | |
{ | |
any str = std::string("Hello, world!"); | |
any i = 10; | |
any p = person("Yudi", 10); | |
any z; | |
any a; | |
try { | |
int i = a.get<int>(); | |
std::cout << "i: " << i << std::endl; | |
}catch(any::exception& ex){ | |
std::cerr << ex.what() << std::endl; | |
} | |
a = 10; | |
a = 25.5; | |
try { | |
p.get<person>().name = "Yudi Matsuzake"; | |
std::cout << str.get<std::string>() << std::endl; | |
std::cout << i.get<int>() << std::endl; | |
std::cout << p.get<person>() << std::endl; | |
std::cout << a.get<double>() << std::endl; | |
std::cout << a.get<float>() << std::endl; | |
std::cout << a.get<int>() << std::endl; | |
}catch(any::invalid_type_convertion& invalid_type){ | |
std::cerr << "Can't convert " << invalid_type.type_from.get() | |
<< " to " << invalid_type.type_to.get() << std::endl; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment