![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* value_description.h -*- C++ -*- 00002 Jeremy Barnes, 29 March 2013 00003 Copyright (c) 2013 Datacratic Inc. All rights reserved. 00004 00005 Code for description and introspection of values and structures. Used 00006 to allow for automated formatters and parsers to be built. 00007 */ 00008 00009 #pragma once 00010 00011 #include <string> 00012 #include <memory> 00013 #include <unordered_map> 00014 #include "jml/arch/exception.h" 00015 #include "jml/arch/demangle.h" 00016 #include "json_parsing.h" 00017 #include "json_printing.h" 00018 #include "value_description_fwd.h" 00019 00020 namespace Datacratic { 00021 00022 struct JsonParsingContext; 00023 struct JsonPrintingContext; 00024 struct JSConverters; 00025 00026 enum class ValueKind : int32_t { 00027 // Atomic, ie all or none is replaced 00028 ATOM, 00029 INTEGER, 00030 FLOAT, 00031 BOOLEAN, 00032 STRING, 00033 ENUM, 00034 00035 // Non-atomic, ie part of them can be mutated 00036 OPTIONAL, 00037 ARRAY, 00038 STRUCTURE, 00039 TUPLE, 00040 VARIANT, 00041 MAP, 00042 ANY 00043 }; 00044 00045 std::ostream & operator << (std::ostream & stream, ValueKind kind); 00046 00047 00048 /*****************************************************************************/ 00049 /* VALUE DESCRIPTION */ 00050 /*****************************************************************************/ 00051 00058 struct ValueDescription { 00059 typedef std::true_type defined; 00060 00061 ValueDescription(ValueKind kind, 00062 const std::type_info * type, 00063 const std::string & typeName = "") 00064 : kind(kind), 00065 type(type), 00066 typeName(typeName.empty() ? ML::demangle(type->name()) : typeName), 00067 jsConverters(nullptr), 00068 jsConvertersInitialized(false) 00069 { 00070 } 00071 00072 ValueKind kind; 00073 const std::type_info * const type; 00074 const std::string typeName; 00075 00076 virtual void parseJson(void * val, JsonParsingContext & context) const = 0; 00077 virtual void printJson(const void * val, JsonPrintingContext & context) const = 0; 00078 virtual bool isDefault(const void * val) const = 0; 00079 virtual void setDefault(void * val) const = 0; 00080 virtual void copyValue(const void * from, void * to) const = 0; 00081 virtual void moveValue(void * from, void * to) const = 0; 00082 virtual void swapValues(void * from, void * to) const = 0; 00083 00084 virtual void * optionalMakeValue(void * val) const 00085 { 00086 throw ML::Exception("type is not optional"); 00087 } 00088 00089 virtual const void * optionalGetValue(const void * val) const 00090 { 00091 throw ML::Exception("type is not optional"); 00092 } 00093 00094 virtual size_t getArrayLength(void * val) const 00095 { 00096 throw ML::Exception("type is not an array"); 00097 } 00098 00099 virtual void * getArrayElement(void * val, uint32_t element) const 00100 { 00101 throw ML::Exception("type is not an array"); 00102 } 00103 00104 virtual const void * getArrayElement(const void * val, uint32_t element) const 00105 { 00106 throw ML::Exception("type is not an array"); 00107 } 00108 00109 virtual void setArrayLength(void * val, size_t newLength) const 00110 { 00111 throw ML::Exception("type is not an array"); 00112 } 00113 00114 virtual const ValueDescription & contained() const 00115 { 00116 throw ML::Exception("type does not contain another"); 00117 } 00118 00119 // Convert from one type to another, making a copy. 00120 // Default will go through a JSON conversion. 00121 virtual void convertAndCopy(const void * from, 00122 const ValueDescription & fromDesc, 00123 void * to) const; 00124 00125 struct FieldDescription { 00126 std::string fieldName; 00127 std::string comment; 00128 std::unique_ptr<ValueDescription > description; 00129 int offset; 00130 int fieldNum; 00131 }; 00132 00133 virtual size_t getFieldCount(const void * val) const 00134 { 00135 throw ML::Exception("type doesn't support fields"); 00136 } 00137 00138 virtual const FieldDescription * 00139 hasField(const void * val, const std::string & name) const 00140 { 00141 throw ML::Exception("type doesn't support fields"); 00142 } 00143 00144 virtual void forEachField(const void * val, 00145 const std::function<void (const FieldDescription &)> & onField) const 00146 { 00147 throw ML::Exception("type doesn't support fields"); 00148 } 00149 00150 virtual const FieldDescription & 00151 getField(const std::string & field) const 00152 { 00153 throw ML::Exception("type doesn't support fields"); 00154 } 00155 00156 // Storage to cache Javascript converters 00157 mutable JSConverters * jsConverters; 00158 mutable bool jsConvertersInitialized; 00159 }; 00160 00161 void registerValueDescription(const std::type_info & type, 00162 std::function<ValueDescription * ()>, 00163 bool isDefault); 00164 00165 template<typename T> 00166 struct RegisterValueDescription { 00167 RegisterValueDescription() 00168 { 00169 registerValueDescription(typeid(T), [] () { return getDefaultDescription((T*)0); }); 00170 } 00171 }; 00172 00173 template<typename T, typename Impl> 00174 struct RegisterValueDescriptionI { 00175 RegisterValueDescriptionI() 00176 : done(false) 00177 { 00178 registerValueDescription(typeid(T), [] () { return new Impl(); }, true); 00179 } 00180 00181 bool done; 00182 }; 00183 00184 #define REGISTER_VALUE_DESCRIPTION(type) \ 00185 namespace { \ 00186 static const RegisterValueDescription<type> registerValueDescription#type; \ 00187 } 00188 00189 00190 /*****************************************************************************/ 00191 /* VALUE DESCRIPTION TEMPLATE */ 00192 /*****************************************************************************/ 00193 00198 template<typename T> 00199 struct ValueDescriptionT : public ValueDescription { 00200 00201 ValueDescriptionT(ValueKind kind = ValueKind::ATOM) 00202 : ValueDescription(kind, &typeid(T)) 00203 { 00204 } 00205 00206 virtual void parseJson(void * val, JsonParsingContext & context) const 00207 { 00208 T * val2 = reinterpret_cast<T *>(val); 00209 return parseJsonTyped(val2, context); 00210 } 00211 00212 virtual void parseJsonTyped(T * val, JsonParsingContext & context) const 00213 { 00214 return parseJson(val, context); 00215 } 00216 00217 virtual void printJson(const void * val, JsonPrintingContext & context) const 00218 { 00219 const T * val2 = reinterpret_cast<const T *>(val); 00220 return printJsonTyped(val2, context); 00221 } 00222 00223 virtual void printJsonTyped(const T * val, JsonPrintingContext & context) const 00224 { 00225 return printJson(val, context); 00226 } 00227 00228 virtual bool isDefault(const void * val) const 00229 { 00230 const T * val2 = reinterpret_cast<const T *>(val); 00231 return isDefaultTyped(val2); 00232 } 00233 00234 virtual bool isDefaultTyped(const T * val) const 00235 { 00236 return false; 00237 } 00238 00239 virtual void setDefault(void * val) const 00240 { 00241 T * val2 = reinterpret_cast<T *>(val); 00242 setDefaultTyped(val2); 00243 } 00244 00245 virtual void setDefaultTyped(T * val) const 00246 { 00247 *val = T(); 00248 } 00249 00250 virtual void copyValue(const void * from, void * to) const 00251 { 00252 auto from2 = reinterpret_cast<const T *>(from); 00253 auto to2 = reinterpret_cast<T *>(to); 00254 if (from2 == to2) 00255 return; 00256 *to2 = *from2; 00257 } 00258 00259 virtual void moveValue(void * from, void * to) const 00260 { 00261 auto from2 = reinterpret_cast<T *>(from); 00262 auto to2 = reinterpret_cast<T *>(to); 00263 if (from2 == to2) 00264 return; 00265 *to2 = std::move(*from2); 00266 } 00267 00268 virtual void swapValues(void * from, void * to) const 00269 { 00270 using std::swap; 00271 auto from2 = reinterpret_cast<T *>(from); 00272 auto to2 = reinterpret_cast<T *>(to); 00273 if (from2 == to2) 00274 return; 00275 std::swap(*from2, *to2); 00276 } 00277 00278 virtual void * optionalMakeValue(void * val) const 00279 { 00280 T * val2 = reinterpret_cast<T *>(val); 00281 return optionalMakeValueTyped(val2); 00282 } 00283 00284 virtual void * optionalMakeValueTyped(T * val) const 00285 { 00286 throw ML::Exception("type is not optional"); 00287 } 00288 00289 virtual const void * optionalGetValue(const void * val) const 00290 { 00291 const T * val2 = reinterpret_cast<const T *>(val); 00292 return optionalGetValueTyped(val2); 00293 } 00294 00295 virtual const void * optionalGetValueTyped(const T * val) const 00296 { 00297 throw ML::Exception("type is not optional"); 00298 } 00299 }; 00300 00301 template<typename T> 00302 ValueDescriptionT<T> * 00303 getDefaultDescription(T * = 0, 00304 typename DefaultDescription<T>::defined * = 0) 00305 { 00306 return new DefaultDescription<T>(); 00307 } 00308 00309 template<typename T, ValueKind kind = ValueKind::ATOM, 00310 typename Impl = DefaultDescription<T> > 00311 struct ValueDescriptionI : public ValueDescriptionT<T> { 00312 00313 static RegisterValueDescriptionI<T, Impl> regme; 00314 00315 ValueDescriptionI() 00316 : ValueDescriptionT<T>(kind) 00317 { 00318 regme.done = true; 00319 } 00320 }; 00321 00322 template<typename T, ValueKind kind, typename Impl> 00323 RegisterValueDescriptionI<T, Impl> 00324 ValueDescriptionI<T, kind, Impl>:: 00325 regme; 00326 00327 template<class Struct> 00328 struct StructureDescription; 00329 00330 inline void * addOffset(void * base, ssize_t offset) 00331 { 00332 char * c = reinterpret_cast<char *>(base); 00333 return c + offset; 00334 } 00335 00336 inline const void * addOffset(const void * base, ssize_t offset) 00337 { 00338 const char * c = reinterpret_cast<const char *>(base); 00339 return c + offset; 00340 } 00341 00342 00343 /*****************************************************************************/ 00344 /* STRUCTURE DESCRIPTION BASE */ 00345 /*****************************************************************************/ 00346 00349 struct StructureDescriptionBase { 00350 00351 StructureDescriptionBase(const std::type_info * type, 00352 const std::string & typeName = "", 00353 bool nullAccepted = false) 00354 : type(type), 00355 typeName(typeName.empty() ? ML::demangle(type->name()) : typeName), 00356 nullAccepted(nullAccepted) 00357 { 00358 } 00359 00360 const std::type_info * const type; 00361 const std::string typeName; 00362 bool nullAccepted; 00363 00364 typedef ValueDescription::FieldDescription FieldDescription; 00365 00366 // Comparison object to allow const char * objects to be looked up 00367 // in the map and so for comparisons to be done with no memory 00368 // allocations. 00369 struct StrCompare { 00370 inline bool operator () (const char * s1, const char * s2) const 00371 { 00372 char c1 = *s1++, c2 = *s2++; 00373 00374 if (c1 < c2) return true; 00375 if (c1 > c2) return false; 00376 if (c1 == 0) return false; 00377 00378 c1 = *s1++; c2 = *s2++; 00379 00380 if (c1 < c2) return true; 00381 if (c1 > c2) return false; 00382 if (c1 == 0) return false; 00383 00384 return strcmp(s1, s2) < 0; 00385 } 00386 00387 }; 00388 00389 typedef std::map<const char *, FieldDescription, StrCompare> Fields; 00390 Fields fields; 00391 00392 std::vector<std::string> fieldNames; 00393 00394 std::vector<Fields::const_iterator> orderedFields; 00395 00396 virtual void parseJson(void * output, JsonParsingContext & context) const 00397 { 00398 if (!onEntry(output, context)) return; 00399 00400 if (nullAccepted && context.isNull()) { 00401 context.expectNull(); 00402 return; 00403 } 00404 00405 if (!context.isObject()) 00406 context.exception("expected structure of type " + typeName); 00407 00408 auto onMember = [&] () 00409 { 00410 //using namespace std; 00411 //cerr << "got field " << context.printPath() << endl; 00412 00413 auto n = context.fieldNamePtr(); 00414 auto it = fields.find(n); 00415 if (it == fields.end()) { 00416 context.onUnknownField(); 00417 } 00418 else { 00419 it->second.description 00420 ->parseJson(addOffset(output, it->second.offset), 00421 context); 00422 } 00423 }; 00424 00425 context.forEachMember(onMember); 00426 00427 onExit(output, context); 00428 } 00429 00430 virtual void printJson(const void * input, JsonPrintingContext & context) const 00431 { 00432 context.startObject(); 00433 00434 for (const auto & it: orderedFields) { 00435 auto & fd = it->second; 00436 00437 auto mbr = addOffset(input, fd.offset); 00438 if (fd.description->isDefault(mbr)) 00439 continue; 00440 context.startMember(it->first); 00441 fd.description->printJson(mbr, context); 00442 } 00443 00444 context.endObject(); 00445 } 00446 00447 virtual bool onEntry(void * output, JsonParsingContext & context) const = 0; 00448 virtual void onExit(void * output, JsonParsingContext & context) const = 0; 00449 }; 00450 00451 00452 /*****************************************************************************/ 00453 /* STRUCTURE DESCRIPTION */ 00454 /*****************************************************************************/ 00455 00456 template<class Struct> 00457 struct StructureDescription 00458 : public ValueDescriptionI<Struct, ValueKind::STRUCTURE, 00459 StructureDescription<Struct> >, 00460 public StructureDescriptionBase { 00461 00462 StructureDescription(bool nullAccepted = false) 00463 : StructureDescriptionBase(&typeid(Struct), "", nullAccepted) 00464 { 00465 } 00466 00468 std::function<bool (Struct *, JsonParsingContext & context)> onEntryHandler; 00469 00471 std::function<void (Struct *, JsonParsingContext & context)> onUnknownField; 00472 00473 virtual bool onEntry(void * output, JsonParsingContext & context) const 00474 { 00475 if (onEntryHandler) { 00476 if (!onEntryHandler((Struct *)output, context)) 00477 return false; 00478 } 00479 00480 if (onUnknownField) 00481 context.onUnknownFieldHandlers.push_back([=,&context] () { this->onUnknownField((Struct *)output, context); }); 00482 00483 return true; 00484 } 00485 00486 virtual void onExit(void * output, JsonParsingContext & context) const 00487 { 00488 if (onUnknownField) 00489 context.onUnknownFieldHandlers.pop_back(); 00490 } 00491 00492 template<typename V, typename Base> 00493 void addField(std::string name, 00494 V Base::* field, 00495 std::string comment, 00496 ValueDescriptionT<V> * description 00497 = getDefaultDescription((V *)0)) 00498 { 00499 if (fields.count(name.c_str())) 00500 throw ML::Exception("field '" + name + "' added twice"); 00501 00502 fieldNames.push_back(name); 00503 const char * fieldName = fieldNames.back().c_str(); 00504 00505 auto it = fields.insert 00506 (Fields::value_type(fieldName, std::move(FieldDescription()))) 00507 .first; 00508 00509 FieldDescription & fd = it->second; 00510 fd.fieldName = fieldName; 00511 fd.comment = comment; 00512 fd.description.reset(description); 00513 Struct * p = nullptr; 00514 fd.offset = (size_t)&(p->*field); 00515 fd.fieldNum = fields.size() - 1; 00516 orderedFields.push_back(it); 00517 //using namespace std; 00518 //cerr << "offset = " << fd.offset << endl; 00519 } 00520 00521 template<typename V> 00522 void addParent(ValueDescriptionT<V> * description_ 00523 = getDefaultDescription((V *)0)) 00524 { 00525 StructureDescription<V> * desc2 00526 = dynamic_cast<StructureDescription<V> *>(description_); 00527 if (!desc2) { 00528 delete description_; 00529 throw ML::Exception("parent description is not a structure"); 00530 } 00531 00532 std::unique_ptr<StructureDescription<V> > description(desc2); 00533 00534 Struct * p = nullptr; 00535 V * p2 = static_cast<V *>(p); 00536 00537 size_t ofs = (size_t)p2; 00538 00539 for (auto & oit: description->orderedFields) { 00540 FieldDescription & ofd = const_cast<FieldDescription &>(oit->second); 00541 const std::string & name = ofd.fieldName; 00542 00543 fieldNames.push_back(name); 00544 const char * fieldName = fieldNames.back().c_str(); 00545 00546 auto it = fields.insert(Fields::value_type(fieldName, std::move(FieldDescription()))).first; 00547 FieldDescription & fd = it->second; 00548 fd.fieldName = fieldName; 00549 fd.comment = ofd.comment; 00550 fd.description = std::move(ofd.description); 00551 00552 fd.offset = ofd.offset + ofs; 00553 fd.fieldNum = fields.size() - 1; 00554 orderedFields.push_back(it); 00555 } 00556 } 00557 00558 virtual size_t getFieldCount(const void * val) const 00559 { 00560 return fields.size(); 00561 } 00562 00563 virtual const FieldDescription * 00564 hasField(const void * val, const std::string & field) const 00565 { 00566 auto it = fields.find(field.c_str()); 00567 if (it != fields.end()) 00568 return &it->second; 00569 return nullptr; 00570 } 00571 00572 virtual void forEachField(const void * val, 00573 const std::function<void (const FieldDescription &)> & onField) const 00574 { 00575 for (auto f: orderedFields) { 00576 onField(f->second); 00577 } 00578 } 00579 00580 virtual const FieldDescription & 00581 getField(const std::string & field) const 00582 { 00583 auto it = fields.find(field.c_str()); 00584 if (it != fields.end()) 00585 return it->second; 00586 throw ML::Exception("structure has no field " + field); 00587 } 00588 00589 virtual void parseJson(void * val, JsonParsingContext & context) const 00590 { 00591 return StructureDescriptionBase::parseJson(val, context); 00592 } 00593 00594 virtual void printJson(const void * val, JsonPrintingContext & context) const 00595 { 00596 return StructureDescriptionBase::printJson(val, context); 00597 } 00598 }; 00599 00600 template<typename Enum> 00601 struct EnumDescription: public ValueDescriptionT<Enum> { 00602 00603 struct Value { 00604 int value; 00605 std::string name; 00606 }; 00607 00608 std::unordered_map<std::string, int> parse; 00609 std::unordered_map<int, Value> print; 00610 }; 00611 00612 template<typename T> 00613 struct ListDescriptionBase { 00614 00615 ListDescriptionBase(ValueDescriptionT<T> * inner = getDefaultDescription((T *)0)) 00616 : inner(inner) 00617 { 00618 } 00619 00620 std::unique_ptr<ValueDescriptionT<T> > inner; 00621 00622 template<typename List> 00623 void parseJsonTypedList(List * val, JsonParsingContext & context) const 00624 { 00625 val->clear(); 00626 00627 if (!context.isArray()) 00628 context.exception("expected array of " + inner->typeName); 00629 00630 auto onElement = [&] () 00631 { 00632 T el; 00633 inner->parseJsonTyped(&el, context); 00634 val->emplace_back(std::move(el)); 00635 }; 00636 00637 context.forEachElement(onElement); 00638 } 00639 00640 template<typename List> 00641 void printJsonTypedList(const List * val, JsonPrintingContext & context) const 00642 { 00643 context.startArray(val->size()); 00644 00645 for (unsigned i = 0; i < val->size(); ++i) { 00646 context.newArrayElement(); 00647 inner->printJsonTyped(&(*val)[i], context); 00648 } 00649 00650 context.endArray(); 00651 } 00652 }; 00653 00654 template<typename T> 00655 struct DefaultDescription<std::vector<T> > 00656 : public ValueDescriptionI<std::vector<T>, ValueKind::ARRAY>, 00657 public ListDescriptionBase<T> { 00658 00659 DefaultDescription(ValueDescriptionT<T> * inner 00660 = getDefaultDescription((T *)0)) 00661 : ListDescriptionBase<T>(inner) 00662 { 00663 } 00664 00665 virtual void parseJson(void * val, JsonParsingContext & context) const 00666 { 00667 std::vector<T> * val2 = reinterpret_cast<std::vector<T> *>(val); 00668 return parseJsonTyped(val2, context); 00669 } 00670 00671 virtual void parseJsonTyped(std::vector<T> * val, JsonParsingContext & context) const 00672 { 00673 this->parseJsonTypedList(val, context); 00674 } 00675 00676 virtual void printJson(const void * val, JsonPrintingContext & context) const 00677 { 00678 const std::vector<T> * val2 = reinterpret_cast<const std::vector<T> *>(val); 00679 return printJsonTyped(val2, context); 00680 } 00681 00682 virtual void printJsonTyped(const std::vector<T> * val, JsonPrintingContext & context) const 00683 { 00684 this->printJsonTypedList(val, context); 00685 } 00686 00687 virtual bool isDefault(const void * val) const 00688 { 00689 const std::vector<T> * val2 = reinterpret_cast<const std::vector<T> *>(val); 00690 return isDefaultTyped(val2); 00691 } 00692 00693 virtual bool isDefaultTyped(const std::vector<T> * val) const 00694 { 00695 return val->empty(); 00696 } 00697 00698 virtual size_t getArrayLength(void * val) const 00699 { 00700 const std::vector<T> * val2 = reinterpret_cast<const std::vector<T> *>(val); 00701 return val2->size(); 00702 } 00703 00704 virtual void * getArrayElement(void * val, uint32_t element) const 00705 { 00706 std::vector<T> * val2 = reinterpret_cast<std::vector<T> *>(val); 00707 return &val2->at(element); 00708 } 00709 00710 virtual const void * getArrayElement(const void * val, uint32_t element) const 00711 { 00712 const std::vector<T> * val2 = reinterpret_cast<const std::vector<T> *>(val); 00713 return &val2->at(element); 00714 } 00715 00716 virtual void setArrayLength(void * val, size_t newLength) const 00717 { 00718 std::vector<T> * val2 = reinterpret_cast<std::vector<T> *>(val); 00719 val2->resize(newLength); 00720 } 00721 00722 virtual const ValueDescription & contained() const 00723 { 00724 return *this->inner; 00725 } 00726 }; 00727 00728 00729 // Template set for which hasToJson<T>::value is true if and only if it has a function 00730 // Json::Value T::toJson() const 00731 template<typename T, typename Enable = void> 00732 struct hasToJson { 00733 enum { value = false }; 00734 }; 00735 00736 template<typename T> 00737 struct hasToJson<T, typename std::enable_if<std::is_convertible<Json::Value, decltype(std::declval<const T>().toJson())>::value>::type> { 00738 enum { value = true }; 00739 }; 00740 00741 // Template set for which hasFromJson<T>::value is true if and only if it has a function 00742 // static T T::fromJson(Json::Value) 00743 template<typename T, typename Enable = void> 00744 struct hasFromJson { 00745 enum { value = false }; 00746 }; 00747 00748 template<typename T> 00749 struct hasFromJson<T, typename std::enable_if<std::is_convertible<T, decltype(T::fromJson(std::declval<Json::Value>()))>::value>::type> { 00750 enum { value = true }; 00751 }; 00752 00753 // jsonDecode implementation for any type which: 00754 // 1) has a default description; 00755 // 2) does NOT have a fromJson() function (there is a simpler overload for this case) 00756 template<typename T> 00757 T jsonDecode(const Json::Value & json, T * = 0, 00758 decltype(getDefaultDescription((T *)0)) * = 0, 00759 typename std::enable_if<!hasFromJson<T>::value>::type * = 0) 00760 { 00761 T result; 00762 00763 static std::unique_ptr<ValueDescription> desc 00764 (getDefaultDescription((T *)0)); 00765 StructuredJsonParsingContext context(json); 00766 desc->parseJson(&result, context); 00767 return result; 00768 } 00769 00770 // jsonEncode implementation for any type which: 00771 // 1) has a default description; 00772 // 2) does NOT have a toJson() function (there is a simpler overload for this case) 00773 template<typename T> 00774 Json::Value jsonEncode(const T & obj, 00775 decltype(getDefaultDescription((T *)0)) * = 0, 00776 typename std::enable_if<!hasToJson<T>::value>::type * = 0) 00777 { 00778 static std::unique_ptr<ValueDescription> desc 00779 (getDefaultDescription((T *)0)); 00780 StructuredJsonPrintingContext context; 00781 desc->printJson(&obj, context); 00782 return std::move(context.output); 00783 } 00784 00785 } // namespace Datacratic