1 /*
2  * Copyright (C) 1998-2018 ALPS Collaboration. See COPYRIGHT.TXT
3  * All rights reserved. Use is subject to license terms. See LICENSE.TXT
4  * For use in publications, see ACKNOWLEDGE.TXT
5  */
6 
7 /** @file dict_value.cpp
8     Contains implementation of some alps::params_ns::dict_value members */
9 
10 #include <boost/version.hpp>
11 
12 #include <alps/params/dict_value.hpp>
13 #include <alps/params/hdf5_variant.hpp>
14 
15 #ifdef ALPS_HAVE_MPI
16 #include <alps/params/mpi_variant.hpp>
17 #endif
18 
19 namespace alps {
20     namespace params_ns {
21 
22         namespace detail {
23             namespace visitor {
24 
25                 /// Visitor to compare 2 value of dict_value type
26                 /** @note Values are comparable iff they are of the same type (FIXME!) */
27                 class comparator2 : public boost::static_visitor<int> {
28                     template <typename A, typename B>
cmp_(const A & a,const B & b)29                     static bool cmp_(const A& a, const B& b) { return (a==b)? 0 : (a<b)? -1:1; }
30 
31                     public:
32                     /// Called by apply_visitor for bound values of different types
33                     template <typename LHS_T, typename RHS_T>
operator ()(const LHS_T & lhs,const RHS_T & rhs) const34                     int operator()(const LHS_T& lhs, const RHS_T& rhs) const {
35                         std::string lhs_name=detail::type_info<LHS_T>::pretty_name();
36                         std::string rhs_name=detail::type_info<RHS_T>::pretty_name();
37                         throw exception::type_mismatch("","Attempt to compare dictionary values containing "
38                                                        "incompatible types "+
39                                                        lhs_name + "<=>" + rhs_name);
40                     }
41 
42                     /// Called by apply_visitor for bound values of the same type
43                     template <typename LHS_RHS_T>
operator ()(const LHS_RHS_T & lhs,const LHS_RHS_T & rhs) const44                     int operator()(const LHS_RHS_T& lhs, const LHS_RHS_T& rhs) const {
45                         return cmp_(lhs,rhs);
46                     }
47 
48                     /// Called by apply_visitor for bound values both having None type
operator ()(const dict_value::None & lhs,const dict_value::None & rhs) const49                     int operator()(const dict_value::None& lhs, const dict_value::None& rhs) const {
50                         return 1;
51                     }
52 
53 
54                     // FIXME:TODO:
55                     // Same types: compare directly
56                     // Integral types: compare using signs (extract it to a separate namespace/class)
57                     // FP types: compare directly
58                     // Everything else: throw
59                 };
60 
61                 /// Visitor to test for exact equality (name and value)
62                 class equals2 : public boost::static_visitor<bool> {
63                     public:
64                     /// Called when bound values have the same type
65                     template <typename LHS_RHS_T>
operator ()(const LHS_RHS_T & lhs,const LHS_RHS_T & rhs) const66                     bool operator()(const LHS_RHS_T& lhs, const LHS_RHS_T& rhs) const {
67                         return lhs==rhs;
68                     }
69 
70                     /// Called when bound types are different
71                     template <typename LHS_T, typename RHS_T>
operator ()(const LHS_T & lhs,const RHS_T & rhs) const72                     bool operator()(const LHS_T& lhs, const RHS_T& rhs) const{
73                         return false;
74                     }
75 
76                     /// Called when LHS is None
77                     template <typename RHS_T>
operator ()(const dict_value::None &,const RHS_T &) const78                     bool operator()(const dict_value::None&, const RHS_T&) const {
79                         return false;
80                     }
81 
82                     /// Called when RHS is None
83                     template <typename LHS_T>
operator ()(const LHS_T &,const dict_value::None &) const84                     bool operator()(const LHS_T&, const dict_value::None&) const {
85                         return false;
86                     }
87 
88                     /// Called when both are None
operator ()(const dict_value::None &,const dict_value::None &) const89                     bool operator()(const dict_value::None&, const dict_value::None&) const {
90                         return true;
91                     }
92                 };
93 
94             } // visitor::
95 
96         } // detail::
97 
compare(const dict_value & rhs) const98         int dict_value::compare(const dict_value& rhs) const
99         {
100             if (this->empty() || rhs.empty()) throw exception::uninitialized_value(name_+"<=>"+rhs.name_,"Attempt to compare uninitialized value");
101 
102             try {
103                 return boost::apply_visitor(detail::visitor::comparator2(), val_, rhs.val_);
104             } catch (exception::exception_base& exc) {
105                 exc.set_name(name_+"<=>"+rhs.name_);
106                 throw;
107             }
108         }
109 
equals(const dict_value & rhs) const110         bool dict_value::equals(const dict_value& rhs) const
111         {
112             return boost::apply_visitor(detail::visitor::equals2(), val_, rhs.val_);
113         }
114 
save(alps::hdf5::archive & ar) const115         void dict_value::save(alps::hdf5::archive& ar) const {
116             if (this->empty()) return;
117             alps::hdf5::write_variant<detail::dict_all_types>(ar, val_);
118         }
119 
load(alps::hdf5::archive & ar)120         void dict_value::load(alps::hdf5::archive& ar) {
121             const std::string context=ar.get_context();
122             std::string::size_type slash_pos=context.find_last_of("/");
123             if (slash_pos==std::string::npos) slash_pos=0; else ++slash_pos;
124             name_=context.substr(slash_pos);
125             val_=alps::hdf5::read_variant<detail::dict_all_types>(ar);
126         }
127 
128         namespace {
129             struct typestring_visitor : public boost::static_visitor<std::string> {
130                 template <typename T>
operator ()alps::params_ns::__anon528a127b0111::typestring_visitor131                 std::string operator()(const T& val) const {
132                     std::string ret=detail::type_info<T>::pretty_name();
133                     return ret;
134                 }
135             };
136 
137             // Printing of a vector
138             // FIXME!!! Consolidate with other definitions and move to alps::utilities
139             template <typename T>
operator <<(std::ostream & strm,const std::vector<T> & vec)140             inline std::ostream& operator<<(std::ostream& strm, const std::vector<T>& vec)
141             {
142                 typedef std::vector<T> vtype;
143                 typedef typename vtype::const_iterator itype;
144 
145                 strm << "[";
146                 itype it=vec.begin();
147                 const itype end=vec.end();
148 
149                 if (end!=it) {
150                     strm << *it;
151                     for (++it; end!=it; ++it) {
152                         strm << ", " << *it;
153                     }
154                 }
155                 strm << "]";
156 
157                 return strm;
158             }
159 
160             struct print_visitor {
161 #if __cplusplus == 201402L && BOOST_VERSION == 105800
162                 // Workaround for a bug in boost 1.58 with C++14.
163                 // Defining `result_type` as a reference type
164                 // leads to a compilation error;
165                 // however, C++14 is able to deduce the type of the result
166                 // without `result_type` being defined.
167 #else
168                 typedef std::ostream& result_type;
169 #endif
170                 std::ostream& os_;
171 
print_visitoralps::params_ns::__anon528a127b0111::print_visitor172                 print_visitor(std::ostream& os) : os_(os) {}
173 
174                 template <typename T>
operator ()alps::params_ns::__anon528a127b0111::print_visitor175                 std::ostream& operator()(const T& val) const {
176                     return os_ << val;
177                 }
178 
operator ()alps::params_ns::__anon528a127b0111::print_visitor179                 std::ostream& operator()(const dict_value::None&) const {
180                     throw std::logic_error("print_visitor: This is not expected to be called");
181                 }
182             };
183 
184         }
185 
print(std::ostream & s,const dict_value & dv,bool terse)186         std::ostream& print(std::ostream& s, const dict_value& dv, bool terse) {
187             if (dv.empty()) {
188                 s << "[NONE]";
189                 if (!terse) s << " (type: None)";
190             } else {
191                 // s << dv.val_;
192                 boost::apply_visitor(print_visitor(s), dv.val_);
193                 if (!terse) s << " (type: " << boost::apply_visitor(typestring_visitor(), dv.val_) << ")";
194             }
195             if (!terse) s << " (name='" << dv.name_ << "')";
196             return s;
197         }
198 
199 #ifdef ALPS_HAVE_MPI
broadcast(const alps::mpi::communicator & comm,int root)200         void dict_value::broadcast(const alps::mpi::communicator& comm, int root)
201         {
202             using alps::mpi::broadcast;
203             broadcast(comm, name_, root);
204             broadcast<detail::dict_all_types>(comm, val_, root);
205         }
206 #endif
207 
208 
209     } // params_ns::
210 } // alps::
211