Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Просьба code review: расширение boost для хранения настроек


Перед вами код моего расширения к библиотеке 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

    


Ответы

Ответ 1



Проблема в том, что сама идея класса 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% состоит ваш код.

Комментариев нет:

Отправить комментарий