| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2000-2022 Inria | ||
| 3 | * All rights reserved. | ||
| 4 | * | ||
| 5 | * Redistribution and use in source and binary forms, with or without | ||
| 6 | * modification, are permitted provided that the following conditions are met: | ||
| 7 | * | ||
| 8 | * * Redistributions of source code must retain the above copyright notice, | ||
| 9 | * this list of conditions and the following disclaimer. | ||
| 10 | * * Redistributions in binary form must reproduce the above copyright notice, | ||
| 11 | * this list of conditions and the following disclaimer in the documentation | ||
| 12 | * and/or other materials provided with the distribution. | ||
| 13 | * * Neither the name of the ALICE Project-Team nor the names of its | ||
| 14 | * contributors may be used to endorse or promote products derived from this | ||
| 15 | * software without specific prior written permission. | ||
| 16 | * | ||
| 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 27 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | * | ||
| 29 | * Contact: Bruno Levy | ||
| 30 | * | ||
| 31 | * https://www.inria.fr/fr/bruno-levy | ||
| 32 | * | ||
| 33 | * Inria, | ||
| 34 | * Domaine de Voluceau, | ||
| 35 | * 78150 Le Chesnay - Rocquencourt | ||
| 36 | * FRANCE | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | |||
| 40 | #ifndef GEOGRAM_BASIC_ENVIRONMENT | ||
| 41 | #define GEOGRAM_BASIC_ENVIRONMENT | ||
| 42 | |||
| 43 | #include <geogram/basic/common.h> | ||
| 44 | #include <geogram/basic/smart_pointer.h> | ||
| 45 | #include <geogram/basic/counted.h> | ||
| 46 | #include <string> | ||
| 47 | #include <vector> | ||
| 48 | #include <map> | ||
| 49 | |||
| 50 | /** | ||
| 51 | * \file geogram/basic/environment.h | ||
| 52 | * \brief Provides a mechanism to store global | ||
| 53 | * variables, retrieve them by their names and | ||
| 54 | * attach observers to them. | ||
| 55 | */ | ||
| 56 | |||
| 57 | namespace GEO { | ||
| 58 | |||
| 59 | class Environment; | ||
| 60 | |||
| 61 | /************************************************************************/ | ||
| 62 | |||
| 63 | /** | ||
| 64 | * \brief Observes Environment variables. | ||
| 65 | * \details VariableObserver offers the possibility to receive | ||
| 66 | * notifications when a variable is changed in an Environment. | ||
| 67 | * | ||
| 68 | * To listen to a variable: | ||
| 69 | * -# Create a class derived class from VariableObserver | ||
| 70 | * and implement function value_changed(). | ||
| 71 | * -# Create a MyObserver instance with name "my_variable", it will be | ||
| 72 | * automatically attached to the variable in the root Environment. | ||
| 73 | * | ||
| 74 | * \code | ||
| 75 | * struct MyObserver : VariableObserver { | ||
| 76 | * MyObserver(const std::string& name) : VariableObserver(name) {} | ||
| 77 | * virtual void value_changed(const std::string& new_value) { | ||
| 78 | * }; | ||
| 79 | * std::auto_ptr<MyObserver> myobserver = | ||
| 80 | * new MyObserver("my_variable"); | ||
| 81 | * \endcode | ||
| 82 | */ | ||
| 83 | class GEOGRAM_API VariableObserver { | ||
| 84 | public: | ||
| 85 | /** | ||
| 86 | * \brief Creates a new variable observer. | ||
| 87 | * \details This creates a new observer for variable \p var_name and | ||
| 88 | * automatically adds itself to the variable's observers in the root | ||
| 89 | * environment. | ||
| 90 | * \param[in] var_name name of the variable observed. | ||
| 91 | */ | ||
| 92 | VariableObserver(const std::string& var_name); | ||
| 93 | |||
| 94 | /** | ||
| 95 | * \brief Receives a change notification. | ||
| 96 | * \details This function is called by the Environment when | ||
| 97 | * the variable observed by this observer is modified. | ||
| 98 | * \param[in] new_value the new value of the observed variable. | ||
| 99 | */ | ||
| 100 | virtual void value_changed(const std::string& new_value) = 0; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * \brief Deletes the observer. | ||
| 104 | * \details This automatically removes this observer from the | ||
| 105 | * root environment. | ||
| 106 | */ | ||
| 107 | virtual ~VariableObserver(); | ||
| 108 | |||
| 109 | /** | ||
| 110 | * \brief Gets the observed variable. | ||
| 111 | * \return The name of the variable observed by this observer. | ||
| 112 | */ | ||
| 113 | const std::string& observed_variable() const { | ||
| 114 | return observed_variable_; | ||
| 115 | } | ||
| 116 | |||
| 117 | private: | ||
| 118 | std::string observed_variable_; | ||
| 119 | Environment* environment_; | ||
| 120 | }; | ||
| 121 | |||
| 122 | /************************************************************************/ | ||
| 123 | |||
| 124 | /** | ||
| 125 | * \brief List of VariableObserver%s | ||
| 126 | * \details List of variable observers are attached to observed variables | ||
| 127 | * in the Environment%s. | ||
| 128 | */ | ||
| 129 | class GEOGRAM_API VariableObserverList { | ||
| 130 | public: | ||
| 131 | /** | ||
| 132 | * \brief Creates an empty list of variable observers. | ||
| 133 | */ | ||
| 134 | ✗ | VariableObserverList() : | |
| 135 | ✗ | block_notify_(false) { | |
| 136 | ✗ | } | |
| 137 | |||
| 138 | /** | ||
| 139 | * \brief Notifies all observers in the list. | ||
| 140 | * \param[in] value the value of the variable being changed. | ||
| 141 | */ | ||
| 142 | void notify_observers(const std::string& value); | ||
| 143 | |||
| 144 | /** | ||
| 145 | * \brief Adds an observer to the list. | ||
| 146 | * This adds observer \p observer at the end of the list only if it is | ||
| 147 | * not already present. | ||
| 148 | * \param[in] observer a pointer to the VariableObserver to add. | ||
| 149 | */ | ||
| 150 | void add_observer(VariableObserver* observer); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * \brief Removes an observer from the list. | ||
| 154 | * \param[in] observer a pointer to the VariableObserver to remove. | ||
| 155 | */ | ||
| 156 | void remove_observer(VariableObserver* observer); | ||
| 157 | |||
| 158 | private: | ||
| 159 | /** List of VariableObserver%s */ | ||
| 160 | typedef std::vector<VariableObserver*> Observers; | ||
| 161 | Observers observers_; | ||
| 162 | bool block_notify_; | ||
| 163 | }; | ||
| 164 | |||
| 165 | /************************************************************************/ | ||
| 166 | |||
| 167 | /** | ||
| 168 | * \brief Application environment | ||
| 169 | * \details | ||
| 170 | * Environment is a flexible framework for storing and retrieving | ||
| 171 | * application properties. Most important client functions are: | ||
| 172 | * - get_value() to retrieve a property | ||
| 173 | * - set_value() to store a property | ||
| 174 | * | ||
| 175 | * By default, the framework provides a single root Environment that can | ||
| 176 | * be accessed with function instance(). This root environment uses a | ||
| 177 | * dictionary for storing application properties as name-value pairs. | ||
| 178 | * | ||
| 179 | * But developers can define custom Environment classes to access | ||
| 180 | * properties differently. For this, the custom Environment classes must | ||
| 181 | * reimplement low-level access functions get_local_value() and | ||
| 182 | * set_local_value(). For instance, one can redefine low-level functions | ||
| 183 | * to: | ||
| 184 | * | ||
| 185 | * - access properties in a file database | ||
| 186 | * - access properties as system environment variables (see | ||
| 187 | * SystemEnvironment) | ||
| 188 | * - expose/control a software module configuration: | ||
| 189 | * - get_local_value() exposes the module configuration as properties | ||
| 190 | * - set_local_value() allows to control the module configuration | ||
| 191 | * through properties. | ||
| 192 | * | ||
| 193 | * This technique is widely used in Vorpaline, for instance: | ||
| 194 | * - Process has a dedicated environment to control Process | ||
| 195 | * configuration (multithreading, FPE, ...) | ||
| 196 | * - Logger also has a dedicated environment to configure the Logger | ||
| 197 | * behavior, allowed features, ... | ||
| 198 | * | ||
| 199 | * Plugging custom Environment%s in the framework is as simple as adding | ||
| 200 | * the custom Environment as a child of the root Environment with function | ||
| 201 | * add_environment(). Setting a property in an environment affects | ||
| 202 | * this environment locally \b only if no child environment can store the | ||
| 203 | * property. Similarily, retrieving a property from an environment first | ||
| 204 | * checks if the property exists locally, then in all child environments. | ||
| 205 | * | ||
| 206 | * In addition, the Environment framework provides a mechanism for being | ||
| 207 | * notified when a property is modified: VariableObserver%s can be | ||
| 208 | * attached to specific properties to capture modifications of their value | ||
| 209 | * (for more details see VariableObserver). | ||
| 210 | */ | ||
| 211 | class GEOGRAM_API Environment : public Counted { | ||
| 212 | public: | ||
| 213 | /** | ||
| 214 | * \brief Gets the root environment | ||
| 215 | * \details If the root environment does not yet exists, it is created | ||
| 216 | * on the fly. | ||
| 217 | * \return A pointer to the root environment | ||
| 218 | */ | ||
| 219 | static Environment* instance(); | ||
| 220 | |||
| 221 | /** | ||
| 222 | * \brief Cleans up the environment | ||
| 223 | * \details This destroys the whole root Environment hierarchy. | ||
| 224 | */ | ||
| 225 | static void terminate(); | ||
| 226 | |||
| 227 | /** | ||
| 228 | * \brief Adds a child environment | ||
| 229 | * \details Environment \p env is added as a child of this | ||
| 230 | * environment which takes ownership of \p env. The child environment | ||
| 231 | * will be deleted when this environment is deleted. | ||
| 232 | * \param[in] env the child environment | ||
| 233 | * \retval true if the child has been successfully added | ||
| 234 | * \retval false otherwise | ||
| 235 | */ | ||
| 236 | virtual bool add_environment(Environment* env); | ||
| 237 | |||
| 238 | /** | ||
| 239 | * \brief Tests if a variable exists | ||
| 240 | * \param[in] name the name of the variable | ||
| 241 | * \retval true if the variable exists | ||
| 242 | * \retval false otherwise | ||
| 243 | */ | ||
| 244 | bool has_value(const std::string& name) const; | ||
| 245 | |||
| 246 | /** | ||
| 247 | * \brief Retrieves the value of a variable | ||
| 248 | * \details Searches variable \p name and stores its value in the | ||
| 249 | * output string \p value. The function first checks if the variable | ||
| 250 | * exists locally, then in all child environments recursively. | ||
| 251 | * \param[in] name the name of the variable | ||
| 252 | * \param[out] value is set the variable value if it was found | ||
| 253 | * either locally or in a child environment. | ||
| 254 | * \retval true if the variable was found | ||
| 255 | * \retval false otherwise | ||
| 256 | */ | ||
| 257 | virtual bool get_value( | ||
| 258 | const std::string& name, std::string& value | ||
| 259 | ) const; | ||
| 260 | |||
| 261 | /** | ||
| 262 | * \brief Retrieves the value of a variable | ||
| 263 | * \details This is a variant of get_value(name, value) that returns | ||
| 264 | * the variable value directly it it exists. If the variable is not | ||
| 265 | * found, then the function calls abort(). | ||
| 266 | * \param[in] name the name of the variable | ||
| 267 | * \return the variable value if it exists. | ||
| 268 | */ | ||
| 269 | std::string get_value(const std::string& name) const; | ||
| 270 | |||
| 271 | /** | ||
| 272 | * \brief Sets a variable value | ||
| 273 | * \details Sets the variable named \p name to the given \p value. The | ||
| 274 | * function first visits all child environments recursively until one | ||
| 275 | * of them accepts the variable. If no child environment can store the | ||
| 276 | * variable, the variable is set locally in this environment. If a | ||
| 277 | * variable is set in an environment, all the variable observers are | ||
| 278 | * notified, starting from the modified environment up to the root | ||
| 279 | * environment. | ||
| 280 | * \param[in] name the name of the variable | ||
| 281 | * \param[in] value the value of the variable | ||
| 282 | * \retval true if the variable was successfully added, either locally | ||
| 283 | * or in a child environment. | ||
| 284 | * \retval false otherwise | ||
| 285 | */ | ||
| 286 | virtual bool set_value( | ||
| 287 | const std::string& name, const std::string& value | ||
| 288 | ); | ||
| 289 | |||
| 290 | /** | ||
| 291 | * \brief Finds the environment that declares a variable as | ||
| 292 | * a local name. | ||
| 293 | * \param[in] name the name of the variable | ||
| 294 | * \return a pointer to the Environment that has \p name as a | ||
| 295 | * local variable, or nullptr if no such environment exists | ||
| 296 | */ | ||
| 297 | virtual Environment* find_environment(const std::string& name); | ||
| 298 | |||
| 299 | /** | ||
| 300 | * \brief Attaches an observer to a variable | ||
| 301 | * \details Adds observer \p observer to the list of observers | ||
| 302 | * attached to variable \p name. If the observer is already attached | ||
| 303 | * to the variable, the function calls abort(). The environment does | ||
| 304 | * \b not take ownership of the observer, it is the responsibility of | ||
| 305 | * the caller to delete all the variable observers added to an | ||
| 306 | * Environment. | ||
| 307 | * \param[in] name the name of the variable | ||
| 308 | * \param[in] observer a variable observer to add | ||
| 309 | * \retval true if \p observer has been successfully added | ||
| 310 | * \retval false otherwise | ||
| 311 | */ | ||
| 312 | virtual bool add_observer( | ||
| 313 | const std::string& name, VariableObserver* observer | ||
| 314 | ); | ||
| 315 | |||
| 316 | /** | ||
| 317 | * \brief Detaches an observer from a variable | ||
| 318 | * \details Removes observer \p observer from the list of observers | ||
| 319 | * attached to variable \p name. If the observer is not attached to | ||
| 320 | * the variable, the function calls abort(). The environment does \b | ||
| 321 | * not delete the removed observer, it is the responsibility of the | ||
| 322 | * caller to delete all the variable observers added to an | ||
| 323 | * Environment. | ||
| 324 | * \param[in] name the name of the variable | ||
| 325 | * \param[in] observer a variable observer to remove | ||
| 326 | * \retval true if \p observer has been successfully removed | ||
| 327 | * \retval false otherwise | ||
| 328 | */ | ||
| 329 | virtual bool remove_observer( | ||
| 330 | const std::string& name, VariableObserver* observer | ||
| 331 | ); | ||
| 332 | |||
| 333 | /** | ||
| 334 | * \brief Notifies observers | ||
| 335 | * \details This notifies the observers attached to variable \p | ||
| 336 | * name in this environment, passing them the current value of the | ||
| 337 | * variable. If \p recursive is set to \c true, then the function | ||
| 338 | * recursively notifies observers in the child contexts. | ||
| 339 | * \param[in] name the name of the variable | ||
| 340 | * \param[in] recursive if \c true, notifies observers in the child | ||
| 341 | * contexts. This is \c false by default. | ||
| 342 | * \return \c true | ||
| 343 | */ | ||
| 344 | virtual bool notify_observers( | ||
| 345 | const std::string& name, bool recursive = false | ||
| 346 | ); | ||
| 347 | |||
| 348 | protected: | ||
| 349 | /** | ||
| 350 | * \brief Environment destructor | ||
| 351 | * \details This deletes all the child environments, but it does \b | ||
| 352 | * not delete the variable observers. | ||
| 353 | */ | ||
| 354 | ~Environment() override; | ||
| 355 | |||
| 356 | /** | ||
| 357 | * \brief Retrieves a variable value locally | ||
| 358 | * \details This function is used internally. It searches variable \p | ||
| 359 | * name \b locally and stores its value in the output string \p value. | ||
| 360 | * \param[in] name the name of the variable | ||
| 361 | * \param[out] value is set the variable value if it was found \b | ||
| 362 | * locally. | ||
| 363 | * \retval true if the variable was found | ||
| 364 | * \retval false if not | ||
| 365 | * \note This function must be reimplemented in derived custom | ||
| 366 | * environments. | ||
| 367 | */ | ||
| 368 | virtual bool get_local_value( | ||
| 369 | const std::string& name, std::string& value | ||
| 370 | ) const = 0; | ||
| 371 | |||
| 372 | /** | ||
| 373 | * \brief Sets a variable value locally | ||
| 374 | * \details This function is used internally. It sets the variable | ||
| 375 | * named \p name to the given \p value \b locally. | ||
| 376 | * \param[in] name the name of the variable | ||
| 377 | * \param[in] value the value of the variable | ||
| 378 | * \retval true if the variable was successfully added \b locally | ||
| 379 | * \retval false otherwise | ||
| 380 | * \note This function must be reimplemented in derived custom | ||
| 381 | * environments. | ||
| 382 | */ | ||
| 383 | virtual bool set_local_value( | ||
| 384 | const std::string& name, const std::string& value | ||
| 385 | ) = 0; | ||
| 386 | |||
| 387 | /** | ||
| 388 | * \brief Notifies observers | ||
| 389 | * \details This function is used internally. It notifies the | ||
| 390 | * observers attached to variable \p name in this environment, passing | ||
| 391 | * them the modified value \p value. If \p recursive is \c true, then | ||
| 392 | * the function recursively notifies observers in the child contexts. | ||
| 393 | * \param[in] name the name of the variable | ||
| 394 | * \param[in] value the modified value | ||
| 395 | * \param[in] recursive if \c true, notifies observers in the child | ||
| 396 | * contexts. | ||
| 397 | * \return \c true | ||
| 398 | */ | ||
| 399 | bool notify_observers( | ||
| 400 | const std::string& name, const std::string& value, | ||
| 401 | bool recursive | ||
| 402 | ); | ||
| 403 | |||
| 404 | /** | ||
| 405 | * \brief Notifies local observers | ||
| 406 | * \details This function is used internally. It notifies the | ||
| 407 | * observers attached to variable \p name in this environment, passing | ||
| 408 | * them the modified value \p value. Observers in child environments | ||
| 409 | * are \b not notified. | ||
| 410 | * \param[in] name the name of the variable | ||
| 411 | * \param[in] value the modified value | ||
| 412 | * \return \c true | ||
| 413 | */ | ||
| 414 | bool notify_local_observers( | ||
| 415 | const std::string& name, const std::string& value | ||
| 416 | ); | ||
| 417 | |||
| 418 | private: | ||
| 419 | /** Smart pointer that contains a Environment object */ | ||
| 420 | typedef SmartPointer<Environment> Environment_var; | ||
| 421 | |||
| 422 | /** List of child environments */ | ||
| 423 | typedef std::vector<Environment_var> Environments; | ||
| 424 | |||
| 425 | /** Stores VariableObserverList indexed by name */ | ||
| 426 | typedef std::map<std::string, VariableObserverList> ObserverMap; | ||
| 427 | |||
| 428 | static Environment_var instance_; | ||
| 429 | Environments environments_; | ||
| 430 | ObserverMap observers_; | ||
| 431 | }; | ||
| 432 | |||
| 433 | /************************************************************************/ | ||
| 434 | |||
| 435 | /** | ||
| 436 | * \brief System environment | ||
| 437 | * \details | ||
| 438 | * This class is a specialization of Environment that retrieves the | ||
| 439 | * variable values from the system environment, but does not allow to set | ||
| 440 | * them. | ||
| 441 | */ | ||
| 442 | class SystemEnvironment : public Environment { | ||
| 443 | protected: | ||
| 444 | /** SystemEnvironment destructor */ | ||
| 445 | ~SystemEnvironment() override; | ||
| 446 | |||
| 447 | /** | ||
| 448 | * \copydoc Environment::set_local_value() | ||
| 449 | * This function does actually \b not update the system environment | ||
| 450 | * and always returns \c false. | ||
| 451 | */ | ||
| 452 | bool set_local_value( | ||
| 453 | const std::string& name, const std::string& value | ||
| 454 | ) override; | ||
| 455 | |||
| 456 | /** | ||
| 457 | * \copydoc Environment::get_local_value() | ||
| 458 | * The value is retrieved from the system environment using the system | ||
| 459 | * function getenv(). | ||
| 460 | */ | ||
| 461 | bool get_local_value( | ||
| 462 | const std::string& name, std::string& value | ||
| 463 | ) const override; | ||
| 464 | }; | ||
| 465 | } | ||
| 466 | |||
| 467 | #endif | ||
| 468 |