Skip to content
This repository
Newer
Older
100644 190 lines (144 sloc) 5.95 kb
27494a20 »
2012-06-02 Pull from FB rev 63ce89e
1 `folly/dynamic.h`
2 -----------------
3
4 `folly/dynamic.h` provides a runtime dynamically typed value for
5 C++, similar to the way languages with runtime type systems work
6 (e.g. Python). It can hold types from a predetermined set of types
7 (ints, bools, arrays of other dynamics, etc), similar to something like
8 `boost::variant`, but the syntax is intended to be a little more like
9 using the native type directly.
10
11 To use `dynamic`, you need to be using gcc 4.6 or later. You'll want to
12 include `folly/dynamic.h` (or perhaps also `folly/json.h`).
13
14 ### Overview
15 ***
16
17 Here are some code samples to get started (assumes a `using
18 folly::dynamic;` was used):
19
20 ``` Cpp
21 dynamic twelve = 12; // creates a dynamic that holds an integer
22 dynamic str = "string"; // yep, this one is an fbstring
23
24 // A few other types.
25 dynamic nul = nullptr;
26 dynamic boolean = false;
27
28 // Arrays can be initialized with brackets.
29 dynamic array = { "array ", "of ", 4, " elements" };
30 assert(array.size() == 4);
31 dynamic emptyArray = {};
32 assert(array.empty());
33
34 // Maps from dynamics to dynamics are called objects. The
35 // dynamic::object constant is how you make an empty map from dynamics
36 // to dynamics.
37 dynamic map = dynamic::object;
38 map["something"] = 12;
39 map["another_something"] = map["something"] * 2;
40
41 // Dynamic objects may be intialized this way
42 dynamic map2 = dynamic::object("something", 12)("another_something", 24);
43 ```
44
45 ### Runtime Type Checking and Conversions
46 ***
47
48 Any operation on a dynamic requires checking at runtime that the
49 type is compatible with the operation. If it isn't, you'll get a
50 `folly::TypeError`. Other exceptions can also be thrown if
51 you try to do something impossible (e.g. if you put a very large
52 64-bit integer in and try to read it out as a double).
53
54 More examples should hopefully clarify this:
55
56 ``` Cpp
57 dynamic dint = 42;
58
59 dynamic str = "foo";
60 dynamic anotherStr = str + "something"; // fine
61 dynamic thisThrows = str + dint; // TypeError is raised
62 ```
63
64 Explicit type conversions can be requested for some of the basic types:
65
66 ``` Cpp
67 dynamic dint = 12345678;
68 dynamic doub = dint.asDouble(); // doub will hold 12345678.0
69 dynamic str = dint.asString(); // str == "12345678"
70
71 dynamic hugeInt = std::numeric_limits<int64_t>::max();
72 dynamic hugeDoub = hugeInt.asDouble(); // throws a folly/Conv.h error,
73 // since it can't fit in a double
74 ```
75
76 ### Iteration and Lookup
77 ***
78
79 You can iterate over dynamic arrays as you would over any C++ sequence container.
80
81 ``` Cpp
82 dynamic array = {2, 3, "foo"};
83
84 for (auto& val : array) {
85 doSomethingWith(val);
86 }
87 ```
88
89 You can iterate over dynamic maps by calling `items()`, `keys()`,
90 `values()`, which behave similarly to the homonymous methods of Python
91 dictionaries.
92
93 ``` Cpp
94 dynamic obj = dynamic::object(2, 3)("hello", "world")("x", 4);
95
96 for (auto& pair : obj.items()) {
97 // Key is pair.first, value is pair.second
98 processKey(pair.first);
99 processValue(pair.second);
100 }
101
102 for (auto& key : obj.keys()) {
103 processKey(key);
104 }
105
106 for (auto& value : obj.values()) {
107 processValue(value);
108 }
109 ```
110
111 You can find an element by key in a dynamic map using the `find()` method,
112 which returns an iterator compatible with `items()`:
113
114 ``` Cpp
115 dynamic obj = dynamic::object(2, 3)("hello", "world")("x", 4);
116
117 auto pos = obj.find("hello");
118 // pos->first is "hello"
119 // pos->second is "world"
120
121 auto pos = obj.find("no_such_key);
122 // pos == obj.items().end()
123 ```
124
125
126 ### Use for JSON
127 ***
128
129 The original motivation for implementing this type was to try to
130 make dealing with json documents in C++ almost as easy as it is
131 in languages with dynamic type systems (php or javascript, etc).
132 The reader can judge whether we're anywhere near that goal, but
133 here's what it looks like:
134
135 ``` Cpp
136 // Parsing JSON strings and using them.
137 std::string jsonDocument = R"({"key":12,"key2":[false, null, true, "yay"]})";
138 dynamic parsed = folly::parseJson(jsonDocument);
139 assert(parsed["key"] == 12);
140 assert(parsed["key2"][0] == false);
141 assert(parsed["key2"][1] == nullptr);
142
143 // Building the same document programatically.
144 dynamic sonOfAJ = dynamic::object
145 ("key", 12)
146 ("key2", { false, nullptr, true, "yay" });
147
148 // Printing. (See also folly::toPrettyJson)
149 auto str = folly::toJson(sonOfAJ);
150 assert(jsonDocument.compare(str) == 0);
151 ```
152
153 ### Performance
154 ***
155
156 Dynamic typing is more expensive than static typing, even when
157 you do it in C++. ;)
158
159 However, some effort has been made to keep `folly::dynamic` and
160 the json (de)serialization at least reasonably performant for
161 common cases. The heap is only used for arrays and objects, and
162 move construction is fully supported. String formatting
163 internally also uses the highly performant `folly::to<>` (see
164 `folly/Conv.h`).
165
166 A trade off to keep in mind though, is that
167 `sizeof(folly::dynamic)` is 64 bytes. You probably don't want to
168 use it if you need to allocate large numbers of them (prefer
169 static types, etc).
170
171 ### Some Design Rationale
172 ***
173
174 **Q. Why is there no default constructor?**
175
176 This is a bit of a limitation of `std::initializer_list<>` for
177 this use case. The expression `dynamic d = {}` is required by the
178 standard to call the default constructor if one exists (the
179 reasoning for this makes sense, since `{}` is part of the concept
180 of "uniform initialization", and is intended for use with things
181 like `std::vector`). It would be surprising if this expression
182 didn't leave `d.isArray()` true, but on the other hand it would
183 also be surprising if `dynamic d` left `d.isArray()` as true. The
184 solution was just to disallow uninitialized dynamics: every
185 dynamic must start out being assigned to some value (or nullptr).
186
187 **Q. Isn't this just a poor imitation of the C# language feature?**
188
189 Pretty much.
Something went wrong with that request. Please try again.