2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * Copyright (c) 2015, Facebook, Inc.
18 * All rights reserved.
20 * This source code is licensed under the BSD-style license found in the
21 * LICENSE file in the root directory of this source tree. An additional grant
22 * of patent rights can be found in the PATENTS file in the same directory.
27 #include <folly/Conv.h>
28 #include <boost/function_types/is_member_pointer.hpp>
29 #include <boost/function_types/parameter_types.hpp>
30 #include <boost/mpl/equal.hpp>
31 #include <boost/mpl/pop_front.hpp>
32 #include <boost/mpl/transform.hpp>
33 #include <boost/mpl/vector.hpp>
37 // Auto-conversion of key/value based on callback signature, documented in
40 class IdentifyCallable {
42 enum class Kind { Function, MemberFunction };
43 template <typename Fn>
44 constexpr static Kind getKind() { return test<Fn>(nullptr); }
46 template <typename Fn>
47 using IsMemFn = typename boost::function_types::template is_member_pointer<
48 decltype(&Fn::operator())
50 template <typename Fn>
51 constexpr static typename std::enable_if<IsMemFn<Fn>::value, Kind>::type
52 test(IsMemFn<Fn>*) { return IdentifyCallable::Kind::MemberFunction; }
54 constexpr static Kind test(...) { return IdentifyCallable::Kind::Function; }
57 template <IdentifyCallable::Kind, typename Fn>
58 struct ArgumentTypesByKind {};
59 template <typename Fn>
60 struct ArgumentTypesByKind<IdentifyCallable::Kind::MemberFunction, Fn> {
61 using type = typename boost::mpl::template pop_front<
62 typename boost::function_types::template parameter_types<
63 decltype(&Fn::operator())
67 template <typename Fn>
68 struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> {
69 using type = typename boost::function_types::template parameter_types<Fn>;
72 template <typename Fn>
74 typename ArgumentTypesByKind<IdentifyCallable::getKind<Fn>(), Fn>::type;
76 // At present, works for lambdas or plain old functions, but can be
77 // extended. The comparison deliberately strips cv-qualifieers and
78 // reference, leaving that choice up to the caller.
79 template <typename Fn, typename... Args>
80 constexpr bool hasArgumentTypes() {
81 using HasArgumentTypes = typename boost::mpl::template equal<
82 typename boost::mpl::template transform<
83 typename boost::mpl::template transform<
85 typename std::template remove_reference<boost::mpl::_1>
87 typename std::template remove_cv<boost::mpl::_1>
89 boost::mpl::vector<Args...>
91 return HasArgumentTypes::value;
93 template <typename... Args>
94 using EnableForArgTypes =
95 typename std::enable_if<hasArgumentTypes<Args...>(), void>::type;
98 template <typename Fn> EnableForArgTypes<Fn>
99 invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) {
103 // 1 argument -- pass only the value
105 // folly::dynamic (no conversion)
106 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic>
107 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
111 template <typename Fn> EnableForArgTypes<Fn, int64_t>
112 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
116 template <typename Fn> EnableForArgTypes<Fn, bool>
117 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
121 template <typename Fn> EnableForArgTypes<Fn, double>
122 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
126 template <typename Fn> EnableForArgTypes<Fn, std::string>
127 invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
132 // 2 arguments -- pass both the key and the value.
135 // Pass the key as folly::dynamic, without conversion
137 // folly::dynamic, folly::dynamic (no conversion of value, either)
138 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, folly::dynamic>
139 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
142 // folly::dynamic, int64_t
143 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, int64_t>
144 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
147 // folly::dynamic, bool
148 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, bool>
149 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
152 // folly::dynamic, double
153 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, double>
154 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
157 // folly::dynamic, std::string
158 template <typename Fn> EnableForArgTypes<Fn, folly::dynamic, std::string>
159 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
163 // Convert the key to std::string.
165 // std::string, folly::dynamic (no conversion of value)
166 template <typename Fn> EnableForArgTypes<Fn, std::string, folly::dynamic>
167 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
170 // std::string, int64_t
171 template <typename Fn> EnableForArgTypes<Fn, std::string, int64_t>
172 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
173 fn(k.asString(), v.asInt());
176 template <typename Fn> EnableForArgTypes<Fn, std::string, bool>
177 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
178 fn(k.asString(), v.asBool());
180 // std::string, double
181 template <typename Fn> EnableForArgTypes<Fn, std::string, double>
182 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
183 fn(k.asString(), v.asDouble());
185 // std::string, std::string
186 template <typename Fn> EnableForArgTypes<Fn, std::string, std::string>
187 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
188 fn(k.asString(), v.asString());
191 // Convert the key to int64_t (good for arrays).
193 // int64_t, folly::dynamic (no conversion of value)
194 template <typename Fn> EnableForArgTypes<Fn, int64_t, folly::dynamic>
195 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
199 template <typename Fn> EnableForArgTypes<Fn, int64_t, int64_t>
200 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
201 fn(k.asInt(), v.asInt());
204 template <typename Fn> EnableForArgTypes<Fn, int64_t, bool>
205 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
206 fn(k.asInt(), v.asBool());
209 template <typename Fn> EnableForArgTypes<Fn, int64_t, double>
210 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
211 fn(k.asInt(), v.asDouble());
213 // int64_t, std::string
214 template <typename Fn> EnableForArgTypes<Fn, int64_t, std::string>
215 invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
216 fn(k.asInt(), v.asString());
218 } // namespace detail
220 template <typename Fn>
221 void DynamicParser::optional(const folly::dynamic& key, Fn fn) {
222 wrapError(&key, [&]() {
223 if (auto vp = value().get_ptr(key)) {
230 // Implementation of DynamicParser template & inline methods.
233 template <typename Fn>
234 void DynamicParser::required(const folly::dynamic& key, Fn fn) {
235 wrapError(&key, [&]() {
236 auto vp = value().get_ptr(key);
238 throw std::runtime_error(folly::to<std::string>(
239 "Couldn't find key ", detail::toPseudoJson(key), " in dynamic object"
246 template <typename Fn>
247 void DynamicParser::objectItems(Fn fn) {
248 wrapError(nullptr, [&]() {
249 for (const auto& kv : value().items()) { // .items() can throw
250 parse(kv.first, kv.second, fn);
255 template <typename Fn>
256 void DynamicParser::arrayItems(Fn fn) {
257 wrapError(nullptr, [&]() {
259 for (const auto& v : value()) { // Iteration can throw
260 parse(i, v, fn); // i => dynamic cannot throw
266 template <typename Fn>
267 void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) {
270 } catch (DynamicParserLogicError& ex) {
271 // When the parser is misused, we throw all the way up to the user,
272 // instead of reporting it as if the input is invalid.
274 } catch (DynamicParserParseError& ex) {
275 // We are just bubbling up a parse error for OnError::THROW.
277 } catch (const std::exception& ex) {
278 reportError(lookup_k, ex);
282 template <typename Fn>
283 void DynamicParser::parse(
284 const folly::dynamic& k, const folly::dynamic& v, Fn fn) {
285 auto guard = stack_.push(k, v); // User code can nest parser calls.
286 wrapError(nullptr, [&]() { detail::invokeForKeyValue(fn, k, v); });
289 inline const folly::dynamic& DynamicParser::ParserStack::key() const {
291 throw DynamicParserLogicError("Only call key() inside parsing callbacks.");
296 inline const folly::dynamic& DynamicParser::ParserStack::value() const{
298 throw DynamicParserLogicError(
299 "Parsing nullptr, or parsing after releaseErrors()"