test_variant.cpp0000664000175000017500000001347411461344314013275 0ustar sergeserge//---------------------------------------------------------------------------- /// \file test_variant.cpp //---------------------------------------------------------------------------- /// \brief Test cases for classes in the variant.hpp and variant_tree.hpp. //---------------------------------------------------------------------------- // Copyright (c) 2010 Serge Aleynikov // Created: 2010-07-10 //---------------------------------------------------------------------------- /* ***** BEGIN LICENSE BLOCK ***** This file may be included in different open-source projects Copyright (C) 2010 Serge Aleynikov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***** END LICENSE BLOCK ***** */ #define BOOST_TEST_MODULE test_variant #include #include #include #include #include #include #include #include "variant.hpp" #include "variant_tree.hpp" #include "verbosity.hpp" using namespace util; namespace { typedef boost::mpl::vector< int,int16_t,long,uint16_t,uint32_t,uint64_t> int_types; typedef boost::mpl::vector< bool,int,int16_t,long,uint16_t,uint32_t,uint64_t,double> valid_types; struct int_require { template void operator() (T t) { typedef typename boost::mpl::find::type iter; typedef typename boost::mpl::if_< boost::is_same::type, iter>, long, T >::type type; t = 1; T expect = static_cast(1); variant v(t); T r = v.get(); BOOST_REQUIRE_EQUAL(expect, r); } }; struct int_require_type { template void operator() (T t) { variant v((T)1); BOOST_REQUIRE_EQUAL(variant::TYPE_INT, v.type()); } }; } BOOST_AUTO_TEST_CASE( test_variant ) { { variant v(true); BOOST_REQUIRE_EQUAL(variant::TYPE_BOOL, v.type()); bool r = boost::get(v); r = v.get(); //bool r = (bool)v; BOOST_REQUIRE_EQUAL(true, r); } { variant v(false); BOOST_REQUIRE_EQUAL(variant::TYPE_BOOL, v.type()); bool r = v.get(); BOOST_REQUIRE_EQUAL(false, r); } // Test all numeric types { int_require test; boost::mpl::for_each(test); boost::mpl::for_each(test); } { variant v(true); BOOST_REQUIRE_EQUAL(variant::TYPE_BOOL, v.type()); } { variant v(1.0); BOOST_REQUIRE_EQUAL(variant::TYPE_DOUBLE, v.type()); } { variant v("test"); BOOST_REQUIRE_EQUAL(variant::TYPE_STRING, v.type()); } { std::string s("test"); variant v(s); BOOST_REQUIRE_EQUAL(variant::TYPE_STRING, v.type()); } } BOOST_AUTO_TEST_CASE( test_variant_tree ) { variant_tree pt; { // Put/get int value pt.put("int value", 3); int int_value = pt.get("int value"); BOOST_REQUIRE_EQUAL(3, int_value); } { // Put/get int value pt.put("long value", 10l); long v = pt.get("long value"); BOOST_REQUIRE_EQUAL(10l, v); } { // Put/get string value pt.put("string value", "foo bar"); std::string string_value = pt.get("string value"); BOOST_REQUIRE_EQUAL("foo bar", string_value); } { // Put/get int value pt.put("bool value", true); bool b = pt.get("bool value"); BOOST_REQUIRE_EQUAL(true, b); } } BOOST_AUTO_TEST_CASE( test_variant_tree_file ) { static const char s_data[] = "test\n" "{\n" " verbose debug\n" " test \"test1\"\n" " report_interval 5\n" " threshold 2.012\n" " overwrite true\n" " address \"229.1.0.1:2000 Line1\"\n" " address \"229.1.0.2:2001 Line2\"\n" "}"; std::stringstream s; s << s_data; variant_tree tree; boost::property_tree::info_parser::read_info(s, tree); if (verbosity::level() > VERBOSE_NONE) boost::property_tree::info_parser::write_info(std::cout, tree); { std::string str = tree.get("test.verbose"); BOOST_REQUIRE_EQUAL("debug", str); } { std::string str = tree.get("test.test"); BOOST_REQUIRE_EQUAL("test1", str); } { int n = tree.get("test.report_interval"); BOOST_REQUIRE_EQUAL(5, n); } { bool n = tree.get("test.overwrite"); BOOST_REQUIRE_EQUAL(true, n); } { double n = tree.get("test.threshold"); BOOST_REQUIRE_EQUAL(2.012, n); } { variant_tree& vt = tree.get_child("test"); int n = vt.count("address"); BOOST_REQUIRE_EQUAL(2, n); std::string str = tree.get("test.address"); BOOST_REQUIRE_EQUAL("229.1.0.1:2000 Line1", str); n = tree.count("test.address"); BOOST_REQUIRE_EQUAL(0, n); } } Makefile0000664000175000017500000000026311732430745011523 0ustar sergesergeBOOST_ROOT=/opt/boost_1_49_0 test_variant: test_variant.cpp g++ -o $@ $< -DBOOST_TEST_DYN_LINK -I. -I $(BOOST_ROOT)/include \ -L $(BOOST_ROOT)/lib -lboost_unit_test_framework variant.hpp0000664000175000017500000002271511732430770012244 0ustar sergeserge//---------------------------------------------------------------------------- /// \file variant.hpp //---------------------------------------------------------------------------- /// \brief This file contains a variant class that represents a subtype of /// boost::variant that can hold integer/bool/double/string values. //---------------------------------------------------------------------------- // Author: Serge Aleynikov // Created: 2010-07-10 //---------------------------------------------------------------------------- /* ***** BEGIN LICENSE BLOCK ***** This file may be included in various open-source projects. Copyright (C) 2010 Serge Aleynikov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***** END LICENSE BLOCK ***** */ #ifndef _UTIL_VARIANT_HPP_ #define _UTIL_VARIANT_HPP_ #include #include #include #include #include #include #include #include #include #include #include #include namespace util { struct null {}; class variant: public boost::variant { typedef boost::variant base; typedef boost::mpl::vector internal_types; struct string_visitor: public boost::static_visitor { std::string operator () (null v) const { return ""; } std::string operator () (bool v) const { return v ? "true" : "false"; } std::string operator () (long v) const { std::stringstream s; s << v; return s.str(); } std::string operator () (double v) const { char buf[128]; snprintf(buf, sizeof(buf)-1, "%f", v); // Remove trailing zeros. for (char* p = buf+strlen(buf); p > buf && *(p-1) != '.'; p--) if (*p == '0') *p = '\0'; return buf; } std::string operator () (const std::string& v) const { return v; } }; template T get(T*) const { return boost::get(*this); } variant get(variant*) const { return variant(*this); } public: typedef boost::mpl::vector< int,int16_t,long,uint16_t,uint32_t,uint64_t> int_types; typedef boost::mpl::joint_view< int_types, boost::mpl::vector > valid_types; typedef boost::mpl::joint_view< int_types, boost::mpl::vector > valid_get_types; enum value_type { TYPE_NULL , TYPE_BOOL , TYPE_INT , TYPE_DOUBLE , TYPE_STRING }; variant() : base(null()) {} explicit variant(bool a) : base(a) {} explicit variant(int16_t a) : base((long)a) {} explicit variant(int a) : base((long)a) {} explicit variant(long a) : base(a) {} explicit variant(uint16_t a) : base((long)a) {} explicit variant(uint32_t a) : base((long)a) {} explicit variant(uint64_t a) : base((long)a) {} explicit variant(double a) : base(a) {} explicit variant(const char* a) : base(std::string(a)) {} explicit variant(const std::string& a) : base(a) {} variant(value_type v, const std::string& a) { from_string(v, a); } void operator= (int16_t a) { *(base*)this = (long)a; } void operator= (int a) { *(base*)this = (long)a; } void operator= (int64_t a) { *(base*)this = (long)a; } void operator= (uint16_t a) { *(base*)this = (long)a; } void operator= (uint32_t a) { *(base*)this = (long)a; } void operator= (uint64_t a) { *(base*)this = (long)a; } void operator= (const char* a) { *(base*)this = std::string(a); } template void operator= (T a) { typedef typename boost::mpl::find::type type_iter; BOOST_STATIC_ASSERT( (!boost::is_same< boost::mpl::end::type, type_iter >::value) ); *(base*)this = a; } value_type type() const { return static_cast(which()); } const char* type_str() const { static const char* s_types[] = { "null", "bool", "int", "double", "string" }; return s_types[type()]; } /// Set value to null. void clear() { *(base*)this = null(); } /// Returns true if the value is null. bool is_null() const { return type() == TYPE_NULL; } bool to_bool() const { return boost::get(*this); } long to_int() const { return boost::get(*this); } long to_float( ) const { return boost::get(*this); } const std::string& to_str() const { return boost::get(*this); } const char* c_str() const { return boost::get( *this).c_str(); } /// Convert value to string. std::string to_string() const { variant::string_visitor v; return boost::apply_visitor(v, *this); } template T get() const { // Make sure given type is a valid variant type. // Note to user: an error raised here indicates that // there's a compile-time attempt to convert a variant to // unsupported type. typedef typename boost::mpl::find::type valid_iter; BOOST_STATIC_ASSERT( (!boost::is_same::type, valid_iter>::value)); // For integers - cast them to long type when fetching from the variant. typedef typename boost::mpl::find::type int_iter; typedef typename boost::mpl::if_< boost::is_same::type, int_iter>, T, long >::type type; type* dummy(NULL); return get(dummy); } void from_string(value_type v, const std::string& a) { switch (v) { case TYPE_NULL: *this = null(); break; case TYPE_BOOL: *this = a == "true" || a == "yes"; break; case TYPE_INT: *this = boost::lexical_cast(a); break; case TYPE_DOUBLE: *this = boost::lexical_cast(a); break; case TYPE_STRING: *this = a; default: throw_type_error(type()); } } bool operator== (const variant& rhs) const { if (type() == TYPE_NULL || rhs.type() == TYPE_NULL || type() != rhs.type()) return false; switch (type()) { case TYPE_BOOL: return to_bool() == rhs.to_bool(); case TYPE_INT: return to_int() == rhs.to_int(); case TYPE_DOUBLE: return to_float() == rhs.to_float(); case TYPE_STRING: return to_string() == rhs.to_string(); default: throw_type_error(type()); return false; // just to pease the compiler } } bool operator< (const variant& rhs) const { if (type() == TYPE_NULL || rhs.type() == TYPE_NULL) return false; if (type() < rhs.type()) return true; if (type() > rhs.type()) return false; switch (type()) { case TYPE_BOOL: return to_bool() < rhs.to_bool(); case TYPE_INT: return to_int() < rhs.to_int(); case TYPE_DOUBLE: return to_float() < rhs.to_float(); case TYPE_STRING: return to_string() < rhs.to_string(); default: throw_type_error(type()); return false; // just to pease the compiler } } bool operator> (const variant& rhs) const { if (type() == TYPE_NULL || rhs.type() == TYPE_NULL) return false; if (type() > rhs.type()) return true; if (type() < rhs.type()) return false; switch (type()) { case TYPE_BOOL: return to_bool() > rhs.to_bool(); case TYPE_INT: return to_int() > rhs.to_int(); case TYPE_DOUBLE: return to_float() > rhs.to_float(); case TYPE_STRING: return to_string() > rhs.to_string(); default: throw_type_error(type()); return false; // just to pease the compiler } } private: static void throw_type_error(value_type v) { std::stringstream s; s << "Unknown type " << v; throw std::runtime_error(s.str()); } }; static inline std::ostream& operator<< (std::ostream& out, const variant& a) { return out << a.to_string(); } } // namespace util #endif // _UTIL_VARIANT_HPP_ variant_tree.hpp0000664000175000017500000002266411732431371013264 0ustar sergeserge//---------------------------------------------------------------------------- /// \file variant_tree.hpp //---------------------------------------------------------------------------- /// \brief This file contains a tree class that can hold variant values. //---------------------------------------------------------------------------- // Author: Serge Aleynikov // Created: 2010-07-10 //---------------------------------------------------------------------------- /* ***** BEGIN LICENSE BLOCK ***** This file may be included in various open-source projects. Copyright (C) 2010 Serge Aleynikov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***** END LICENSE BLOCK ***** */ #ifndef _UTIL_VARIANT_TREE_HPP_ #define _UTIL_VARIANT_TREE_HPP_ #include //#include #include #include #include #include #include namespace boost { namespace property_tree { // Custom translator that works with util::variant instead of std::string. // This translator is used to read/write values from files. template <> struct translator_between { typedef translator_between type; typedef std::string external_type; typedef util::variant internal_type; boost::optional get_value(const internal_type& value) const { return boost::optional( value.type() == internal_type::TYPE_NULL ? "" : value.to_string()); } boost::optional put_value(const external_type& value) const { try { long n = lexical_cast(value); for(external_type::const_iterator it=value.begin(), end=value.end(); it!=end; ++it) if (*it < '0' || *it > '9') throw false; return boost::optional(n); } catch (...) {} try { double n = lexical_cast(value); return boost::optional(n); } catch (...) {} if (value == "true" || value == "false") return boost::optional(value[0] == 't'); return boost::optional(value); } }; } // namespace property_tree } // namespace boost namespace util { namespace detail { // Custom translator that works with variant instead of std::string // This translator is used to get/put values through explicit get/put calls. template struct variant_translator { typedef Ext external_type; typedef variant internal_type; /* typedef boost::mpl::joint_view > valid_non_string_types; */ typedef variant::valid_types valid_types; external_type get_value(const internal_type& value) const { return value.get(); } template typename boost::disable_if< boost::is_same< boost::mpl::end::type, boost::mpl::find >, internal_type>::type put_value(T value) const { return variant(value); } }; typedef boost::property_tree::basic_ptree< std::string, // Key type variant, // Data type std::less // Key comparison > basic_variant_tree; } // namespace detail class variant_tree : public detail::basic_variant_tree { typedef detail::basic_variant_tree base; typedef variant_tree self_type; public: typedef boost::property_tree::ptree_bad_path bad_path; variant_tree() {} template static void read_info(Source& src, variant_tree& tree) { boost::property_tree::info_parser::read_info(src, tree); } template static void write_info(Target& tar, variant_tree& tree) { boost::property_tree::info_parser::write_info(tar, tree); } template static void write_info(Target& tar, variant_tree& tree, const Settings& tt) { boost::property_tree::info_parser::write_info(tar, tree, tt); } template T get_value() const { using boost::throw_exception; if(boost::optional o = base::get_value_optional(detail::variant_translator())) { return *o; } BOOST_PROPERTY_TREE_THROW(boost::property_tree::ptree_bad_data( std::string("conversion of data to type \"") + typeid(T).name() + "\" failed", base::data())); } template T get_value(const T& default_value) const { return base::get_value(default_value, detail::variant_translator()); } std::string get_value(const char* default_value) const { return base::get_value(std::string(default_value), detail::variant_translator()); } template boost::optional get_value_optional() const { return base::get_value(detail::variant_translator()); } template void put_value(const T& value) { base::put_value(value, detail::variant_translator()); } template T get(const path_type& path) const { try { return base::get_child(path).BOOST_NESTED_TEMPLATE get_value(detail::variant_translator()); } catch (boost::bad_get& e) { throw bad_path("Cannot convert value to type", path); } } template T get(const path_type& path, const T& default_value) const { try { return base::get(path, default_value, detail::variant_translator()); } catch (boost::bad_get& e) { throw bad_path("Wrong or missing value type", path); } } std::string get(const path_type& path, const char* default_value) const { return base::get(path, std::string(default_value), detail::variant_translator()); } template boost::optional get_optional(const path_type& path) const { return base::get_optional(path, detail::variant_translator()); } template void put(const path_type& path, const T& value) { base::put(path, value, detail::variant_translator()); } template self_type& add(const path_type& path, const T& value) { return static_cast( base::add(path, value, detail::variant_translator())); } void swap(variant_tree& rhs) { base::swap(rhs); } void swap(boost::property_tree::basic_ptree< std::string, variant, std::less >& rhs) { base::swap(rhs); } self_type &get_child(const path_type &path) { return static_cast(base::get_child(path)); } /** Get the child at the given path, or throw @c ptree_bad_path. */ const self_type &get_child(const path_type &path) const { return static_cast(base::get_child(path)); } /** Get the child at the given path, or return @p default_value. */ self_type &get_child(const path_type &path, self_type &default_value) { return static_cast(base::get_child(path, default_value)); } /** Get the child at the given path, or return @p default_value. */ const self_type &get_child(const path_type &path, const self_type &default_value) const { return static_cast(base::get_child(path, default_value)); } /** Get the child at the given path, or return boost::null. */ boost::optional get_child_optional(const path_type &path) { boost::optional o = base::get_child_optional(path); if (!o) return boost::optional(); return boost::optional(static_cast(*o)); } /** Get the child at the given path, or return boost::null. */ boost::optional get_child_optional(const path_type &path) const { boost::optional o = base::get_child_optional(path); if (!o) return boost::optional(); return boost::optional(static_cast(*o)); } self_type &put_child(const path_type &path, const self_type &value) { return static_cast(base::put_child(path, value)); } }; } // namespace util #endif // _UTIL_VARIANT_TREE_HPP_ verbosity.hpp0000664000175000017500000000511211461342365012617 0ustar sergeserge//---------------------------------------------------------------------------- /// \file verbosity.hpp //---------------------------------------------------------------------------- /// This file contains a class verbosty that controls verbosity setting /// based on the value of the VERBOSE environment variable. //---------------------------------------------------------------------------- // Copyright (c) 2010 Omnibius, LLC // Author: Serge Aleynikov // Created: 2010-06-21 //---------------------------------------------------------------------------- /* ***** BEGIN LICENSE BLOCK ***** This file may be included in various open-source projects. Copyright (C) 2010 Serge Aleynikov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***** END LICENSE BLOCK ***** */ #ifndef _UTIL_VERBOSITY_HPP_ #define _UTIL_VERBOSITY_HPP_ #include namespace util { enum verbose_type { VERBOSE_NONE , VERBOSE_TEST , VERBOSE_DEBUG , VERBOSE_INFO , VERBOSE_MESSAGE , VERBOSE_WIRE , VERBOSE_TRACE }; class verbosity { public: static verbose_type level() { static verbose_type verbose = parse(getenv("VERBOSE")); return verbose; } static verbose_type parse(const char* a_verbosity) { const char* p = a_verbosity; if (!p || p[0] == '\0') return VERBOSE_NONE; int n = atoi(p); if (n == 1 || strncmp("test", p, 4) == 0) return VERBOSE_TEST; if (n == 2 || strncmp("debug", p, 3) == 0) return VERBOSE_DEBUG; else if (n == 3 || strncmp("info", p, 4) == 0) return VERBOSE_INFO; else if (n == 4 || strncmp("message", p, 4) == 0) return VERBOSE_MESSAGE; else if (n == 5 || strncmp("wire", p, 4) == 0) return VERBOSE_WIRE; else if (n >= 6 || strncmp("trace", p, 4) == 0) return VERBOSE_TRACE; else return VERBOSE_NONE; } }; } // namespace util #endif // _UTIL_VERBOSITY_HPP_