Skip to content

Instantly share code, notes, and snippets.

@yudi-matsuzake
Last active June 13, 2017 14:06
Show Gist options
  • Save yudi-matsuzake/1ae4dcdad9716122bf19a3a6d8d20d18 to your computer and use it in GitHub Desktop.
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
/** @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;
};
#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