Просьба code review: расширение boost для хранения настроек
1,00
р.
р.
Перед вами код моего расширения к библиотеке boost, который играет роль, схожую с классом QSettings в Qt, разве что реализовано все более в духе boost. Поддерживает произвольные значения для полей, их сериализацию и строгую visitor-based поддержку типов. Хотелось бы услышать замечания по коду, что можно было сделать лучше и т.п, короче, устройте мне code review. Если кому-то интересно, то могу запостить код юнит-тестов.
#include #include #include #include #include #include #include #include #include #include "core_defines.h" #include "core_extensions_lexical_cast.h" namespace boost { namespace property_tree { // Generic value holder, which will attempt to cast objects rather than // indicate error if wrong type was supplied. This behavior is likely to // be more error-prone, but is more 'user-friendly'. See 'strict_value_holder' // class if you need strict type support. // Supports trivial integral, floating point and string types in 'strict' // mode and also allows the storage to hold arbitrary values using // 'boost::any' storage. Note that due to implementation details, // values of specific (non-default) types can't be serialized // (we skip them / replace with dummy objects). class value_holder { // Main value holder type, which has support for common types // and can use the internal visitor for casting. typedef boost::variant Holder public: // Models 'DefaultConstructible', default constructor // leaves the storage uninitialized. value_holder() : using_any_holder_(false) { } template value_holder(const T& value, typename boost::enable_if< typename boost::mpl::contains >::type* = 0) : holder_(value), using_any_holder_(false) { } // Custom constructing routine for string-like types. explicit value_holder(const char* value) : holder_(std::string(value)), using_any_holder_(false) { } // Custom constructing routine for string-like types. explicit value_holder(const wchar_t* value) : holder_(std::wstring(value)), using_any_holder_(false) { } // Custom constructing routine for non-standard types. template value_holder(const T& value, typename boost::disable_if< typename boost::mpl::contains >::type* = 0) : any_holder_(value), using_any_holder_(true) { } // Retrieves held value with possible additional type casts. // Note that this method won't even compile for unsupported // types. template typename boost::enable_if, T>::type as() const { // Apply internal casting visitor. return boost::apply_visitor(type_casting_visitor(), holder_) } // Attempts to retrieve non-standard type from internal 'boost::any'-based // storage. Throws 'boost::bad_any_cast' on errors. template typename boost::disable_if, T>::type as() const { // Apply internal 'boost::any_cast' routine. return boost::any_cast(any_holder_) } // Generic holder swapping (required by 'boost::property_tree' // specification). void swap(value_holder& value) { holder_.swap(value.holder_) any_holder_.swap(value.any_holder_) std::swap(using_any_holder_, value.using_any_holder_) } // Check if current holder is empty (required by // 'boost::property_tree' specification). bool empty() const { // Dispatch emptiness check based on currently // used value holder. if (using_any_holder_) return any_holder_.empty() return holder_.empty() } private: // Internal functional object used to retrieve common types and perform // appropriate casting. template struct type_casting_visitor : boost::static_visitor { // Handles every possible arithmetic type with // 'boost::numeric_cast'. template T operator()(const Y& value) const { return boost::numeric_cast(value) } // Template specialization for 'std::string' -> type casting template <> inline T operator()(const std::string& value) const { return boost::lexical_cast(value) } // Template specialization for 'std::wstring' -> type casting template <> inline T operator()(const std::wstring& value) const { return boost::lexical_cast(value) } } // Template specialization for type -> 'std::string' casting. template <> struct type_casting_visitor : boost::static_visitor { template std::string operator()(const Y& value) const { return boost::lexical_cast(value) } } // Template specialization for type -> 'std::wstring' casting. template <> struct type_casting_visitor : boost::static_visitor { template std::wstring operator()(const Y& value) const { return boost::lexical_cast(value) } } public: // Custom serialization implementation. template void serialize(Archive& ar, const unsigned int) { using namespace ::boost ar & serialization::make_nvp("using_any_holder", using_any_holder_) // Serialize only what we can - if the value shares one // of the predefined types, use the default 'boost::variant' // serialization routine. if (!using_any_holder_) ar & serialization::make_nvp("holder", holder_) } private: // Main value holder instance. Holder holder_ // Alternative value holder, which is used to store // objects and data that differ from supported by main holder. // These can actually be treated as temporary objects, because // due to language / implementation limitations, they aren't // serialized / deserialized (it's impossible to combine benefits // from 'generics' while still having the strict typing - which // is a requirement for our serialization routines). boost::any any_holder_ // Indicates if the current value is actually using a 'boost::any'- // based value holder (non-common value). bool using_any_holder_ } // Strict variation of generic value holder, which // does not support arbitrary types and will // throw if types do not match exactly. class strict_value_holder : public value_holder { // Main value holder type, which has support for common types. typedef boost::variant Holder public: // Models 'DefaultConstructible'. strict_value_holder() { } // Custom constructors for any of the available basic cases (which are // supported by initial 'boost::variant' placeholder). template strict_value_holder(const T& value, typename boost::enable_if< typename boost::mpl::contains >::type* = 0) : holder_(value) { } // Custom constructing routine for string-like types. explicit strict_value_holder(const char* value) : holder_(std::string(value)) { } // Custom constructing routine for string-like types. explicit strict_value_holder(const wchar_t* value) : holder_(std::wstring(value)) { } // Retrieves held value without any type casts. Throws 'boost::bad_get' if // casting was unsuccessful. template typename boost::enable_if, T>::type as() const { return boost::get(holder_) } // Generic holder swapping (required by 'boost::property_tree' // specification). void swap(strict_value_holder& value) { holder_.swap(value.holder_) } // Check if current holder is empty (required by // 'boost::property_tree' specification). bool empty() const { return holder_.empty() } public: // Custom serialization implementation. template void serialize(Archive& ar, const unsigned int) { using namespace ::boost // Serialize the value holder. ar & serialization::make_nvp("holder", holder_) } private: // Main value holder instance. Holder holder_ } // Custom holder-based translator implementation. template struct value_holder_translator { // Type definitions required by the // 'Translator' concept. typedef T internal_type typedef Y external_type boost::optional get_value(const T& value_holder) const { // Attempt to use casting routine // specific for template type. return value_holder.as() } boost::optional put_value(const Y& value) const { // Construct appropriate value holder for specified // value (doesn't throw). return T(value) } } // Set custom translator for internal 'value_holder' type. template struct translator_between { typedef value_holder_translator type } // Set custom translator for internal 'strict_value_holder' type. template struct translator_between { typedef value_holder_translator type } } // namespace property_tree // Type definition for custom holder-based properties collection. Note that // for convenience, properties collection is brought to '::boost' namespace. // This kind of properties allows arbitrary parameter types and, therefore, // will substitute unsupported parameter types with dummies when writing. Also // note, that if the parameter structure is filled from some xml-like file, // you will lose the performance of custom value holder, because in this // case parameters are stored as strings (that obviously brings a huge // casting overhead to 'get' operations). // If you want to save the benefits of type checking and remove type casting // overhead, use serialization instead of raw xml writing / reading. typedef property_tree::basic_ptree< std::string, property_tree::value_holder> properties // This variation of properties structure supports // wide character keys. typedef property_tree::basic_ptree< std::wstring, property_tree::value_holder> wproperties // Bring property-specific path declaration into the '::boost' // namespace. using property_tree::path using property_tree::wpath // Enable custom tree-like format parsers. using property_tree::xml_parser::write_xml using property_tree::xml_parser::read_xml using property_tree::json_parser::write_json using property_tree::json_parser::read_json using property_tree::ini_parser::write_ini using property_tree::ini_parser::read_ini using property_tree::info_parser::write_info using property_tree::info_parser::read_info } // namespace boost
Ответ Проблема в том, что сама идея класса QSettings бесполезна. The QSettings class provides persistent platform-independent application settings. Что в реальности мешает вашей Windows программе выкачать "application settings" в стиле Linux из текстового файла (ini, conf, XML, json, e.t.c), а не из реестра, при этом используется более распространенная библиотека iostream.h, а не специфичная Qt библиотека QSettings.h? P.S. При таком подходе обеспечивается поддержка любой платформы, в которой есть файловая система, и вообще отпадает необходимость в обобщенном программировании и шаблонах, из которых на 95% состоит ваш код.