The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

535 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. /**
  20. Options that control conversion from arbitrary types to juce::var.
  21. @see ToVar
  22. @tags{Core}
  23. */
  24. class ToVarOptions
  25. {
  26. public:
  27. /** By default, conversion will serialise the type using the marshallingVersion defined for
  28. that type. Setting an explicit version allows the type to be serialised as an earlier
  29. version.
  30. */
  31. [[nodiscard]] ToVarOptions withExplicitVersion (std::optional<int> x) const { return withMember (*this, &ToVarOptions::explicitVersion, x); }
  32. /** By default, conversion will include version information for any type with a non-null
  33. marshallingVersion. Setting versionIncluded to false will cause the version info to be
  34. omitted, which is useful in situations where the version information is not needed
  35. (e.g. when presenting transient information to the user, rather than writing data to
  36. disk that must be deserialised in the future).
  37. */
  38. [[nodiscard]] ToVarOptions withVersionIncluded (bool x) const { return withMember (*this, &ToVarOptions::versionIncluded, x); }
  39. /** @see withExplicitVersion() */
  40. [[nodiscard]] auto getExplicitVersion() const { return explicitVersion; }
  41. /** @see withVersionIncluded(). */
  42. [[nodiscard]] auto getVersionIncluded() const { return versionIncluded; }
  43. private:
  44. std::optional<std::optional<int>> explicitVersion;
  45. bool versionIncluded = true;
  46. };
  47. /**
  48. Allows converting an object of arbitrary type to var.
  49. To use this, you must first ensure that the type passed to convert is set up for serialisation.
  50. For details of what this entails, see the docs for SerialisationTraits.
  51. In short, the constant 'marshallingVersion', and either the single function 'serialise()', or
  52. the function pair 'load()' and 'save()' must be defined for the type. These may be defined
  53. as public members of the type T itself, or as public members of juce::SerialisationTraits<T>,
  54. which is a specialisation of the SerialisationTraits template struct for the type T.
  55. @see FromVar
  56. @tags{Core}
  57. */
  58. class ToVar
  59. {
  60. public:
  61. using Options = ToVarOptions;
  62. /** Attempts to convert the argument to a var using the serialisation utilities specified for
  63. that type.
  64. This will return a non-null optional if conversion succeeds, or nullopt if conversion fails.
  65. */
  66. template <typename T>
  67. static std::optional<var> convert (const T& t, const Options& options = {})
  68. {
  69. return Visitor::convert (t, options);
  70. }
  71. private:
  72. class Visitor
  73. {
  74. public:
  75. template <typename T>
  76. static std::optional<var> convert (const T& t, const Options& options)
  77. {
  78. constexpr auto fallbackVersion = detail::ForwardingSerialisationTraits<T>::marshallingVersion;
  79. const auto versionToUse = options.getExplicitVersion()
  80. .value_or (fallbackVersion);
  81. if (versionToUse > fallbackVersion)
  82. {
  83. // The requested explicit version is higher than the declared version of the type.
  84. return std::nullopt;
  85. }
  86. Visitor visitor { versionToUse, options.getVersionIncluded() };
  87. detail::doSave (visitor, t);
  88. return visitor.value;
  89. }
  90. std::optional<int> getVersion() const { return version; }
  91. template <typename... Ts>
  92. void operator() (Ts&&... ts)
  93. {
  94. (visit (std::forward<Ts> (ts)), ...);
  95. }
  96. private:
  97. Visitor (const std::optional<int>& explicitVersion, bool includeVersion)
  98. : version (explicitVersion),
  99. value ([&]() -> var
  100. {
  101. if (! (version.has_value() && includeVersion))
  102. return var();
  103. auto obj = std::make_unique<DynamicObject>();
  104. obj->setProperty ("__version__", *version);
  105. return obj.release();
  106. }()),
  107. versionIncluded (includeVersion) {}
  108. template <typename T>
  109. void visit (const T& t)
  110. {
  111. if constexpr (std::is_integral_v<T>)
  112. {
  113. push ((int64) t);
  114. }
  115. else if constexpr (std::is_floating_point_v<T>)
  116. {
  117. push ((double) t);
  118. }
  119. else if (auto converted = convert (t))
  120. {
  121. push (*converted);
  122. }
  123. else
  124. {
  125. value.reset();
  126. }
  127. }
  128. template <typename T>
  129. void visit (const Named<T>& named)
  130. {
  131. if (! value.has_value())
  132. return;
  133. if (value == var())
  134. value = new DynamicObject;
  135. auto* obj = value->getDynamicObject();
  136. if (obj == nullptr)
  137. {
  138. // Serialisation failure! This may be caused by archiving a primitive or
  139. // SerialisationSize, and then attempting to archive a named pair to the same
  140. // archive instance.
  141. // When using named pairs, *all* items serialised with a particular archiver must be
  142. // named pairs.
  143. jassertfalse;
  144. value.reset();
  145. return;
  146. }
  147. if (! trySetProperty (*obj, named))
  148. value.reset();
  149. }
  150. template <typename T>
  151. void visit (const SerialisationSize<T>&)
  152. {
  153. push (Array<var>{});
  154. }
  155. void visit (const bool& t)
  156. {
  157. push (t);
  158. }
  159. void visit (const String& t)
  160. {
  161. push (t);
  162. }
  163. void visit (const var& t)
  164. {
  165. push (t);
  166. }
  167. template <typename T>
  168. std::optional<var> convert (const T& t)
  169. {
  170. return convert (t, Options{}.withVersionIncluded (versionIncluded));
  171. }
  172. void push (var v)
  173. {
  174. if (! value.has_value())
  175. return;
  176. if (*value == var())
  177. *value = v;
  178. else if (auto* array = value->getArray())
  179. array->add (v);
  180. else
  181. value.reset();
  182. }
  183. template <typename T>
  184. bool trySetProperty (DynamicObject& obj, const Named<T>& n)
  185. {
  186. if (const auto converted = convert (n.value))
  187. {
  188. obj.setProperty (Identifier (std::string (n.name)), *converted);
  189. return true;
  190. }
  191. return false;
  192. }
  193. std::optional<int> version;
  194. std::optional<var> value;
  195. bool versionIncluded = true;
  196. };
  197. };
  198. //==============================================================================
  199. /**
  200. Allows converting a var to an object of arbitrary type.
  201. To use this, you must first ensure that the type passed to convert is set up for serialisation.
  202. For details of what this entails, see the docs for SerialisationTraits.
  203. In short, the constant 'marshallingVersion', and either the single function 'serialise()', or
  204. the function pair 'load()' and 'save()' must be defined for the type. These may be defined
  205. as public members of the type T itself, or as public members of juce::SerialisationTraits<T>,
  206. which is a specialisation of the SerialisationTraits template struct for the type T.
  207. @see ToVar
  208. @tags{Core}
  209. */
  210. class FromVar
  211. {
  212. public:
  213. /** Attempts to convert a var to an instance of type T.
  214. This will return a non-null optional if conversion succeeds, or nullopt if conversion fails.
  215. */
  216. template <typename T>
  217. static std::optional<T> convert (const var& v)
  218. {
  219. return Visitor::convert<T> (v);
  220. }
  221. private:
  222. class Visitor
  223. {
  224. public:
  225. template <typename T>
  226. static std::optional<T> convert (const var& v)
  227. {
  228. const auto version = [&]() -> std::optional<int>
  229. {
  230. if (auto* obj = v.getDynamicObject())
  231. if (obj->hasProperty ("__version__"))
  232. return (int) obj->getProperty ("__version__");
  233. return std::nullopt;
  234. }();
  235. Visitor visitor { version, v };
  236. T t{};
  237. detail::doLoad (visitor, t);
  238. return ! visitor.failed ? std::optional<T> (std::move (t))
  239. : std::nullopt;
  240. }
  241. std::optional<int> getVersion() const { return version; }
  242. template <typename... Ts>
  243. void operator() (Ts&&... ts)
  244. {
  245. (visit (std::forward<Ts> (ts)), ...);
  246. }
  247. private:
  248. Visitor (std::optional<int> vn, const var& i)
  249. : version (vn), input (i) {}
  250. template <typename T>
  251. void visit (T& t)
  252. {
  253. if constexpr (std::is_integral_v<T>)
  254. {
  255. readPrimitive (std::in_place_type<int64>, t);
  256. }
  257. else if constexpr (std::is_floating_point_v<T>)
  258. {
  259. readPrimitive (std::in_place_type<double>, t);
  260. }
  261. else
  262. {
  263. auto node = getNodeToRead();
  264. if (! node.has_value())
  265. return;
  266. auto converted = convert<T> (*node);
  267. if (converted.has_value())
  268. t = *converted;
  269. else
  270. failed = true;
  271. }
  272. }
  273. template <typename T>
  274. void visit (const Named<T>& named)
  275. {
  276. auto node = getNodeToRead();
  277. if (! node.has_value())
  278. return;
  279. auto* obj = node->getDynamicObject();
  280. failed = obj == nullptr || ! tryGetProperty (*obj, named);
  281. }
  282. template <typename T>
  283. void visit (const SerialisationSize<T>& t)
  284. {
  285. if (failed)
  286. return;
  287. if (auto* array = input.getArray())
  288. {
  289. t.size = static_cast<T> (array->size());
  290. currentArrayIndex = 0;
  291. }
  292. else
  293. {
  294. failed = true;
  295. }
  296. }
  297. void visit (bool& t)
  298. {
  299. readPrimitive (std::in_place_type<bool>, t);
  300. }
  301. void visit (String& t)
  302. {
  303. readPrimitive (std::in_place_type<String>, t);
  304. }
  305. void visit (var& t)
  306. {
  307. t = input;
  308. }
  309. static std::optional<double> pullTyped (std::in_place_type_t<double>, const var& source)
  310. {
  311. return source.isDouble() ? std::optional<double> ((double) source) : std::nullopt;
  312. }
  313. static std::optional<int64> pullTyped (std::in_place_type_t<int64>, const var& source)
  314. {
  315. return source.isInt() || source.isInt64() ? std::optional<int64> ((int64) source) : std::nullopt;
  316. }
  317. static std::optional<bool> pullTyped (std::in_place_type_t<bool>, const var& source)
  318. {
  319. return std::optional<bool> ((bool) source);
  320. }
  321. static std::optional<String> pullTyped (std::in_place_type_t<String>, const var& source)
  322. {
  323. return source.isString() ? std::optional<String> (source.toString()) : std::nullopt;
  324. }
  325. std::optional<var> getNodeToRead()
  326. {
  327. if (failed)
  328. return std::nullopt;
  329. if (currentArrayIndex == std::numeric_limits<size_t>::max())
  330. return input;
  331. const auto* array = input.getArray();
  332. if (array == nullptr)
  333. return input;
  334. if ((int) currentArrayIndex < array->size())
  335. return array->getReference ((int) currentArrayIndex++);
  336. failed = true;
  337. return std::nullopt;
  338. }
  339. template <typename TypeToRead, typename T>
  340. void readPrimitive (std::in_place_type_t<TypeToRead> tag, T& t)
  341. {
  342. auto node = getNodeToRead();
  343. if (! node.has_value())
  344. return;
  345. auto typed = pullTyped (tag, *node);
  346. if (typed.has_value())
  347. t = static_cast<T> (*typed);
  348. else
  349. failed = true;
  350. }
  351. template <typename T>
  352. static bool tryGetProperty (const DynamicObject& obj, const Named<T>& n)
  353. {
  354. const Identifier identifier (String (n.name.data(), n.name.size()));
  355. if (! obj.hasProperty (identifier))
  356. return false;
  357. const auto converted = convert<T> (obj.getProperty (identifier));
  358. if (! converted.has_value())
  359. return false;
  360. n.value = *converted;
  361. return true;
  362. }
  363. std::optional<int> version;
  364. var input;
  365. size_t currentArrayIndex = std::numeric_limits<size_t>::max();
  366. bool failed = false;
  367. };
  368. };
  369. //==============================================================================
  370. /**
  371. This template-overloaded class can be used to convert between var and custom types.
  372. If not specialised, the variant converter will attempt to use serialisation functions
  373. if they are detected for the given type.
  374. For details of what this entails, see the docs for SerialisationTraits.
  375. In short, the constant 'marshallingVersion', and either the single function 'serialise()', or
  376. the function pair 'load()' and 'save()' must be defined for the type. These may be defined
  377. as public members of the type T itself, or as public members of juce::SerialisationTraits<T>,
  378. which is a specialisation of the SerialisationTraits template struct for the type T.
  379. @see ToVar, FromVar
  380. @tags{Core}
  381. */
  382. template <typename Type>
  383. struct VariantConverter
  384. {
  385. static Type fromVar (const var& v)
  386. {
  387. return static_cast<Type> (v);
  388. }
  389. static var toVar (const Type& t)
  390. {
  391. return t;
  392. }
  393. };
  394. #ifndef DOXYGEN
  395. template <>
  396. struct VariantConverter<String>
  397. {
  398. static String fromVar (const var& v) { return v.toString(); }
  399. static var toVar (const String& s) { return s; }
  400. };
  401. #endif
  402. /**
  403. A helper type that can be used to implement specialisations of VariantConverter that use
  404. FromVar::convert and ToVar::convert internally.
  405. If you've already implemented SerialisationTraits for a specific type, and don't want to write
  406. a custom VariantConverter that duplicates that implementation, you can instead write:
  407. @code
  408. template <>
  409. struct juce::VariantConverter<MyType> : public juce::StrictVariantConverter<MyType> {};
  410. @endcode
  411. @tags{Core}
  412. */
  413. template <typename Type>
  414. struct StrictVariantConverter
  415. {
  416. static_assert (detail::serialisationKind<Type> != detail::SerialisationKind::none);
  417. static Type fromVar (const var& v)
  418. {
  419. auto converted = FromVar::convert<Type> (v);
  420. jassert (converted.has_value());
  421. return std::move (converted).value_or (Type{});
  422. }
  423. static var toVar (const Type& t)
  424. {
  425. auto converted = ToVar::convert<> (t);
  426. jassert (converted.has_value());
  427. return std::move (converted).value_or (var{});
  428. }
  429. };
  430. } // namespace juce