Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
AlinaUtils.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* AlinaUtils.cc (C) 2000-2026 */
9/* */
10/* Classes utilitaires. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/alina/AlinaUtils.h"
15
16#include "arccore/base/Convert.h"
17
18#include "arccore/common/JSONReader.h"
19#include "arccore/common/JSONWriter.h"
20
21#include "arccore/alina/CSRMatrixView.h"
22
23#include <charconv>
24#include <fstream>
25#include <list>
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30namespace Arcane::Alina::detail
31{
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
46{
47 public:
48
49 std::string data;
50 bool has_data = false;
51 std::list<std::pair<std::string, PropertyTreeImpl>> children;
52
53 public:
54
55 bool empty() const { return !has_data && children.empty(); }
56
57 public:
58
60 PropertyTreeImpl* findChild(const std::string& name)
61 {
62 for (auto& c : children)
63 if (c.first == name)
64 return &c.second;
65 return nullptr;
66 }
67 const PropertyTreeImpl* findChild(const std::string& name) const
68 {
69 for (const auto& c : children)
70 if (c.first == name)
71 return &c.second;
72 return nullptr;
73 }
74
76 size_t count(const std::string& name) const
77 {
78 size_t n = 0;
79 for (const auto& c : children)
80 if (c.first == name)
81 ++n;
82 return n;
83 }
84
86 bool eraseChild(const std::string& name)
87 {
88 auto it = children.begin();
89 while (it != children.end()) {
90 if (it->first == name) {
91 children.erase(it);
92 return true;
93 }
94 ++it;
95 }
96 return false;
97 }
98
99 public:
100
103 PropertyTreeImpl* findNode(const std::string& path)
104 {
105 if (path.empty())
106 return this;
107 PropertyTreeImpl* n = this;
108 for (const auto& part : _splitPath(path)) {
109 n = n->findChild(part);
110 if (!n)
111 return nullptr;
112 }
113 return n;
114 }
115 const PropertyTreeImpl* findNode(const std::string& path) const
116 {
117 if (path.empty())
118 return this;
119 const PropertyTreeImpl* n = this;
120 for (const auto& part : _splitPath(path)) {
121 n = n->findChild(part);
122 if (!n)
123 return nullptr;
124 }
125 return n;
126 }
127
129 const std::string* getValue(const std::string& path) const
130 {
131 const PropertyTreeImpl* n = findNode(path);
132 if (n && n->has_data)
133 return &n->data;
134 return nullptr;
135 }
136
137 public:
138
141 void put(const std::string& path, const std::string& value)
142 {
143 PropertyTreeImpl* n = _createNode(path);
144 n->data = value;
145 n->has_data = true;
146 }
147
150 void addChild(const std::string& path, const PropertyTreeImpl& subtree)
151 {
152 std::vector<std::string> parts = _splitPath(path);
153 if (parts.empty())
154 return;
155 std::string parent_path;
156 for (size_t i = 0; i + 1 < parts.size(); ++i) {
157 if (!parent_path.empty())
158 parent_path += '.';
159 parent_path += parts[i];
160 }
161 PropertyTreeImpl* parent = _createNode(parent_path);
162 parent->children.emplace_back(std::move(parts.back()), subtree);
163 }
164
165 private:
166
167 static std::vector<std::string> _splitPath(const std::string& path)
168 {
169 std::vector<std::string> parts;
170 std::string current;
171 for (char c : path) {
172 if (c == '.') {
173 parts.push_back(current);
174 current.clear();
175 }
176 else {
177 current += c;
178 }
179 }
180 parts.push_back(current);
181 return parts;
182 }
183
185 PropertyTreeImpl* _createNode(const std::string& path)
186 {
187 PropertyTreeImpl* n = this;
188 for (const auto& part : _splitPath(path)) {
189 PropertyTreeImpl* next = n->findChild(part);
190 if (!next)
191 next = &n->children.emplace_back(part, PropertyTreeImpl()).second;
192 n = next;
193 }
194 return n;
195 }
196};
197
198/*---------------------------------------------------------------------------*/
199/*---------------------------------------------------------------------------*/
200
201const PropertyTreeImpl& empty_ptree()
202{
203 static const PropertyTreeImpl p;
204 return p;
205}
206
207// Pour accéder à PropertyTree::m_property_tree en tant que 'detail::PropertyTreeImpl'
209{
210 public:
211
212 static const PropertyTreeImpl& toImpl(const PropertyTree& p)
213 {
214 return *(static_cast<const PropertyTreeImpl*>(p.m_property_tree));
215 }
216 static PropertyTreeImpl& toImpl(PropertyTree& p)
217 {
218 return *(static_cast<PropertyTreeImpl*>(p.m_property_tree));
219 }
220};
221
222/*---------------------------------------------------------------------------*/
223/*---------------------------------------------------------------------------*/
224
225} // namespace Arcane::Alina::detail
226
227/*---------------------------------------------------------------------------*/
228/*---------------------------------------------------------------------------*/
229
230namespace
231{
232using namespace Arcane;
233using namespace Arcane::Alina;
235const PropertyTreeImpl& toImpl(const PropertyTree& p)
236{
237 return detail::PropertyWrapper::toImpl(p);
238}
239const PropertyTreeImpl& toImpl(const PropertyTree* p)
240{
241 return detail::PropertyWrapper::toImpl(*p);
242}
243PropertyTreeImpl& toImpl(PropertyTree* p)
244{
245 return detail::PropertyWrapper::toImpl(*p);
246}
247
248/*---------------------------------------------------------------------------*/
249/*---------------------------------------------------------------------------*/
250
251std::string realToString(double v)
252{
253 char buf[64];
254 auto res = std::to_chars(buf, buf + sizeof(buf), v);
255 return std::string(buf, res.ptr);
256}
257
258bool tryParseInt64(const std::string& s, Int64& v)
259{
261 if (!x)
262 return false;
263 v = *x;
264 return true;
265}
266
267bool tryParseDouble(const std::string& s, double& v)
268{
270 if (!x)
271 return false;
272 v = Convert::toDouble(*x);
273 return true;
274}
275
276// 'true' et 'false' sont acceptés pour faciliter la lecture des
277// paramètres booléens stockés dans un fichier JSON.
278Int64 parseInteger(const std::string& s, const char* name)
279{
280 if (s == "true")
281 return 1;
282 if (s == "false")
283 return 0;
284 Int64 v = 0;
285 if (!tryParseInt64(s, v))
286 ARCCORE_FATAL("Impossible de convertir la valeur '{0}' en entier pour le paramètre '{1}'", s, name);
287 return v;
288}
289
290Int32 parseInt32(const std::string& s, const char* name)
291{
292 Int64 v = parseInteger(s, name);
293 if (v < static_cast<Int64>(std::numeric_limits<Int32>::min()) ||
294 v > static_cast<Int64>(std::numeric_limits<Int32>::max()))
295 ARCCORE_FATAL("La valeur '{0}' est hors plage pour le paramètre '{1}'", s, name);
296 return static_cast<Int32>(v);
297}
298
299double parseReal(const std::string& s, const char* name)
300{
301 if (s == "true")
302 return 1.0;
303 if (s == "false")
304 return 0.0;
305 double v = 0.0;
306 if (!tryParseDouble(s, v))
307 ARCCORE_FATAL("Impossible de convertir la valeur '{0}' en réel pour le paramètre '{1}'", s, name);
308 return v;
309}
310
311void* parsePointer(const std::string& s, const char* name)
312{
313 if (s.empty())
314 return nullptr;
315 // La valeur est un nombre hexadécimal avec un préfixe '0x' optionnel.
317 if (!x)
318 ARCCORE_FATAL("Impossible de convertir la valeur '{0}' en pointeur pour le paramètre '{1}'", s, name);
319 return reinterpret_cast<void*>(static_cast<std::uintptr_t>(*x));
320}
321
322std::string pointerToString(const void* p)
323{
324 std::ostringstream ostr;
325 ostr << "0x" << std::hex << reinterpret_cast<std::uintptr_t>(p);
326 return ostr.str();
327}
328
329/*---------------------------------------------------------------------------*/
330/*---------------------------------------------------------------------------*/
331
332PropertyTreeImpl nodeFromJson(const JSONValue& v)
333{
334 PropertyTreeImpl n;
335 if (v.isObject()) {
336 for (const auto& kv : v.keyValueChildren()) {
337 std::string name(kv.name().toStdStringView());
338 n.children.emplace_back(std::move(name), nodeFromJson(kv.value()));
339 }
340 }
341 else if (v.isArray()) {
342 for (const auto& e : v.valueAsArray())
343 n.children.emplace_back("", nodeFromJson(e));
344 }
345 else if (v.isString()) {
346 n.data = std::string(v.valueAsStringView().toStdStringView());
347 n.has_data = true;
348 }
349 else if (v.isBool()) {
350 n.data = v.valueAsBool() ? "true" : "false";
351 n.has_data = true;
352 }
353 else if (v.isNumber()) {
354 if (v.isInt64()) {
355 n.data = std::to_string(v.valueAsInt64());
356 }
357 else if (v.isUint64()) {
358 UInt64 u = v.valueAsUInt64();
359 if (u <= static_cast<UInt64>(std::numeric_limits<Int64>::max()))
360 n.data = std::to_string(static_cast<Int64>(u));
361 else
362 n.data = realToString(static_cast<double>(u));
363 }
364 else {
365 n.data = realToString(v.valueAsReal());
366 }
367 n.has_data = true;
368 }
369 // Une valeur 'null' produit un nœud vide.
370 return n;
371}
372
373/*---------------------------------------------------------------------------*/
374/*---------------------------------------------------------------------------*/
375
376void writeKeyValue(JSONWriter& w, const StringView& key, const std::string& s)
377{
378 if (s == "true") {
379 w.write(key, true);
380 return;
381 }
382 if (s == "false") {
383 w.write(key, false);
384 return;
385 }
386 Int64 i = 0;
387 if (tryParseInt64(s, i)) {
388 w.write(key, i);
389 return;
390 }
391 double d = 0.0;
392 if (tryParseDouble(s, d)) {
393 w.write(key, d);
394 return;
395 }
396 w.write(key, StringView(s));
397}
398
399void writeJsonNode(JSONWriter& w, const PropertyTreeImpl& n)
400{
401 if (n.children.empty()) {
402 w.writeValue(n.has_data ? StringView(n.data) : StringView());
403 return;
404 }
405 bool is_array = true;
406 for (const auto& c : n.children)
407 if (!c.first.empty()) {
408 is_array = false;
409 break;
410 }
411 if (is_array) {
412 w.beginArray();
413 for (const auto& c : n.children)
414 writeJsonNode(w, c.second);
415 w.endArray();
416 }
417 else {
418 w.beginObject();
419 for (const auto& c : n.children) {
420 String key(c.first);
421 if (c.second.children.empty()) {
422 writeKeyValue(w, key, c.second.has_data ? c.second.data : std::string());
423 }
424 else {
425 if (!key.empty()) {
426 w.writeKey(key);
427 writeJsonNode(w, c.second);
428 }
429 }
430 }
431 w.endObject();
432 }
433}
434
435} // namespace
436
437/*---------------------------------------------------------------------------*/
438/*---------------------------------------------------------------------------*/
439
440namespace Arcane::Alina
441{
442
443/*---------------------------------------------------------------------------*/
444/*---------------------------------------------------------------------------*/
445
446PropertyTree::
447PropertyTree(PropertyTreeImpl* p, bool is_own)
448: m_property_tree(p)
449, m_is_own(is_own)
450{
451}
452
453PropertyTree::
454PropertyTree()
455: m_property_tree(new PropertyTreeImpl())
456, m_is_own(true)
457{
458}
459
460/*---------------------------------------------------------------------------*/
461/*---------------------------------------------------------------------------*/
462
463PropertyTree::
464PropertyTree(const PropertyTree& rhs)
465{
466 if (rhs.m_is_own) {
467 m_property_tree = new PropertyTreeImpl(toImpl(rhs));
468 m_is_own = true;
469 }
470 else {
471 m_property_tree = rhs.m_property_tree;
472 m_is_own = false;
473 }
474}
475
476/*---------------------------------------------------------------------------*/
477/*---------------------------------------------------------------------------*/
478
479PropertyTree& PropertyTree::
480operator=(const PropertyTree& rhs)
481{
482 // Toujours faire une copie indépendante. En particulier, rhs peut être une
483 // vue fille non propriétaire dans cet arbre, donc la copier avant de libérer l'arbre courant.
484 auto* new_property_tree = new PropertyTreeImpl(toImpl(rhs));
485 if (m_is_own)
486 delete &toImpl(this);
487 m_property_tree = new_property_tree;
488 m_is_own = true;
489 return *this;
490}
491
492/*---------------------------------------------------------------------------*/
493/*---------------------------------------------------------------------------*/
494
495PropertyTree::
496~PropertyTree()
497{
498 if (m_is_own)
499 delete &toImpl(this);
500}
501
502/*---------------------------------------------------------------------------*/
503/*---------------------------------------------------------------------------*/
504
505PropertyTree PropertyTree::
506get_child_empty(const std::string& path) const
507{
508 const PropertyTreeImpl* child = toImpl(this).findNode(path);
509 if (!child)
510 child = &detail::empty_ptree();
511 PropertyTree p(const_cast<PropertyTreeImpl*>(child),false);
512 return p;
513}
514
515/*---------------------------------------------------------------------------*/
516/*---------------------------------------------------------------------------*/
517
518bool PropertyTree::
519erase(const char* name)
520{
521 return toImpl(this).eraseChild(name);
522}
523
524/*---------------------------------------------------------------------------*/
525/*---------------------------------------------------------------------------*/
526
527size_t PropertyTree::
528count(const char* name) const
529{
530 return toImpl(this).count(name);
531}
532
533/*---------------------------------------------------------------------------*/
534/*---------------------------------------------------------------------------*/
535
536void PropertyTree::
537read_json(const std::string& filename)
538{
539 std::ifstream is(filename, std::ios::binary);
540 if (!is)
541 ARCCORE_FATAL("Impossible de lire le fichier JSON '{0}'", filename);
542 std::string content((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
543
544 JSONDocument doc;
545 Span<const Byte> bytes(reinterpret_cast<const Byte*>(content.data()), content.size());
546 doc.parse(bytes, StringView(filename));
547
548 toImpl(this) = nodeFromJson(doc.root());
549}
550
551/*---------------------------------------------------------------------------*/
552/*---------------------------------------------------------------------------*/
553
554Int32 PropertyTree::get(const char* param_type, Int32 default_value) const
555{
556 const std::string* s = toImpl(this).getValue(param_type);
557 if (!s)
558 return default_value;
559 return parseInt32(*s, param_type);
560}
561Int64 PropertyTree::get(const char* param_type, Int64 default_value) const
562{
563 const std::string* s = toImpl(this).getValue(param_type);
564 if (!s)
565 return default_value;
566 return parseInteger(*s, param_type);
567}
568double PropertyTree::get(const char* param_type, double default_value) const
569{
570 const std::string* s = toImpl(this).getValue(param_type);
571 if (!s)
572 return default_value;
573 return parseReal(*s, param_type);
574}
575double* PropertyTree::get(const char* param_type, double* default_value) const
576{
577 const std::string* s = toImpl(this).getValue(param_type);
578 if (!s)
579 return default_value;
580 return static_cast<double*>(parsePointer(*s, param_type));
581}
582void* PropertyTree::get(const char* param_type, void* default_value) const
583{
584 const std::string* s = toImpl(this).getValue(param_type);
585 if (!s)
586 return default_value;
587 return parsePointer(*s, param_type);
588}
589std::string PropertyTree::get(const char* param_type, const std::string& default_value) const
590{
591 const std::string* s = toImpl(this).getValue(param_type);
592 if (!s)
593 return default_value;
594 return *s;
595}
596
597void PropertyTree::put(const std::string& path, Int32 value)
598{
599 toImpl(this).put(path, std::to_string(value));
600}
601void PropertyTree::put(const std::string& path, Int64 value)
602{
603 toImpl(this).put(path, std::to_string(value));
604}
605void PropertyTree::put(const std::string& path, double value)
606{
607 toImpl(this).put(path, realToString(value));
608}
609void PropertyTree::put(const std::string& path, const std::string& value)
610{
611 toImpl(this).put(path, value);
612}
613void PropertyTree::put(const std::string& path, double* value)
614{
615 toImpl(this).put(path, pointerToString(value));
616}
617void PropertyTree::put(const std::string& path, void* value)
618{
619 toImpl(this).put(path, pointerToString(value));
620}
621
622/*---------------------------------------------------------------------------*/
623/*---------------------------------------------------------------------------*/
624
625void PropertyTree::
626_addChild(const std::string& path, const char* name,
627 const PropertyTree& obj)
628{
629 toImpl(this).addChild(std::string(path) + name, toImpl(obj));
630}
631
632/*---------------------------------------------------------------------------*/
633/*---------------------------------------------------------------------------*/
634
635void PropertyTree::
636check_params(const std::set<std::string>& names) const
637{
638 const auto& p = toImpl(this);
639 bool has_error = false;
640 for (const auto& n : names) {
641 if (!p.count(n)) {
642 ARCCORE_ALINA_PARAM_MISSING(n);
643 }
644 }
645 for (const auto& v : p.children) {
646 if (!names.count(v.first)) {
647 std::cerr << "AVERTISSEMENT: paramètre inconnu " << v.first << "\n";
648 has_error = true;
649 }
650 }
651 if (has_error)
652 ARCCORE_FATAL("Paramètres invalides");
653}
654
655/*---------------------------------------------------------------------------*/
656/*---------------------------------------------------------------------------*/
657
658void PropertyTree::
659check_params(const std::set<std::string>& names,
660 const std::set<std::string>& opt_names) const
661{
662 const auto& p = toImpl(this);
663 bool has_error = false;
664
665 for (const auto& n : names) {
666 if (!p.count(n)) {
667 ARCCORE_ALINA_PARAM_MISSING(n);
668 }
669 }
670 for (const auto& n : opt_names) {
671 if (!p.count(n)) {
672 ARCCORE_ALINA_PARAM_MISSING(n);
673 }
674 }
675 for (const auto& v : p.children) {
676 if (!names.count(v.first) && !opt_names.count(v.first)) {
677 std::cerr << "AVERTISSEMENT: paramètre inconnu " << v.first << "\n";
678 has_error = true;
679 }
680 }
681 if (has_error)
682 ARCCORE_FATAL("Paramètres invalides");
683}
684
685/*---------------------------------------------------------------------------*/
686/*---------------------------------------------------------------------------*/
687
688void PropertyTree::
689putKeyValue(const std::string& param)
690{
691 size_t eq_pos = param.find('=');
692 if (eq_pos == std::string::npos)
693 ARCCORE_FATAL("Le paramètre dans put() doit avoir le format \"clé=valeur\" (param='{0}')", param);
694 toImpl(this).put(param.substr(0, eq_pos), param.substr(eq_pos + 1));
695}
696
697/*---------------------------------------------------------------------------*/
698/*---------------------------------------------------------------------------*/
699
700detail::empty_params::
701empty_params(const PropertyTree& ap)
702{
703 const auto& p = toImpl(ap);
704 for (const auto& v : p.children) {
705 std::cerr << "Alina: paramètre inconnu " << v.first << "\n";
706 }
707}
708
709/*---------------------------------------------------------------------------*/
710/*---------------------------------------------------------------------------*/
711
712std::ostream& operator<<(std::ostream& o, const PropertyTree& obj)
713{
714 JSONWriter writer(JSONWriter::FormatFlags::None);
715 writeJsonNode(writer, toImpl(obj));
716 o << writer.getBuffer();
717 return o;
718}
719
720/*---------------------------------------------------------------------------*/
721/*---------------------------------------------------------------------------*/
722
723} // namespace Arcane::Alina
724
725/*---------------------------------------------------------------------------*/
726/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Classe pour stocker les paramètres sous forme d'arbre hiérarchique clé/valeur.
Definition AlinaUtils.h:113
Arbre de propriétés hiérarchique minimal avec valeurs de feuilles en chaîne de caractères.
Definition AlinaUtils.cc:46
PropertyTreeImpl * findChild(const std::string &name)
Premier enfant direct avec le nom name ou nullptr s'il n'est pas trouvé.
Definition AlinaUtils.cc:60
size_t count(const std::string &name) const
Nombre d'enfants directs avec le nom name.
Definition AlinaUtils.cc:76
void put(const std::string &path, const std::string &value)
const std::string * getValue(const std::string &path) const
Valeur de feuille au chemin séparé par '.' path ou nullptr s'il n'est pas trouvé.
PropertyTreeImpl * findNode(const std::string &path)
void addChild(const std::string &path, const PropertyTreeImpl &subtree)
PropertyTreeImpl * _createNode(const std::string &path)
Nœud au chemin séparé par '.' path, créant les nœuds intermédiaires.
bool eraseChild(const std::string &name)
Supprime le premier enfant direct avec le nom name.
Definition AlinaUtils.cc:86
static std::optional< T > tryParse(StringView s)
Convertit s au type T.
void parse(Span< const Byte > bytes, Int16 flags=ParseNoFlags)
Lit le fichier au format UTF-8.
JSONValue root() const
Elément racine.
bool isUint64() const
Vrai si la valeur est un entier dans la plage 'UInt64'.
UInt64 valueAsUInt64() const
Valeur sous forme de UInt64. Retourn 0 si 'null()' est vrai.
Real valueAsReal() const
Valeur sous forme de Real. Retourn 0.0 si 'null()' est vrai.
bool valueAsBool() const
Valeur sous forme de booléen. Retourn false si 'null()' est vrai.
Int64 valueAsInt64() const
Valeur sous forme de Int64. Retourn 0 si 'null()' est vrai.
bool isInt64() const
Vrai si la valeur est un entier dans la plage 'Int64'.
StringView valueAsStringView() const
Valeur sous forme de StringView. La chaîne est vide si 'null()' est vrai.
bool isBool() const
Vrai si la valeur est un booléen.
bool isString() const
Vrai si la valeur est une chaîne de caractères.
bool isNumber() const
Vrai si la valeur est un nombre.
Vue sur une chaîne de caractères UTF-8.
Definition StringView.h:47
std::string_view toStdStringView() const ARCCORE_NOEXCEPT
Retourne une vue de la STL de la vue actuelle.
Definition StringView.h:112
constexpr bool empty() const ARCCORE_NOEXCEPT
Vrai si la chaîne est nulle ou vide.
Definition StringView.h:105
Chaîne de caractères unicode.
double toDouble(Real r)
Convertit un Real en double.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::uint64_t UInt64
Type entier non signé sur 64 bits.
std::int64_t Int64
Type entier signé sur 64 bits.
unsigned char Byte
Type d'un octet.
Definition BaseTypes.h:42
std::int32_t Int32
Type entier signé sur 32 bits.