1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_ENV_SETTING_H
25 #define PXR_BASE_TF_ENV_SETTING_H
26 
27 /// \file tf/envSetting.h
28 /// Environment setting variable.
29 ///
30 /// A \c TfEnvSetting<T> is used to access an environment variable that
31 /// controls program execution according to the value set in the environment.
32 /// Currently, the legal types for T are bool, int, and string.
33 ///
34 /// The TfEnvSetting facility is used to enable new features in the code that
35 /// are still in "experimental" mode, and warn the user and/or QA that they
36 /// are pushing the edge of the envelope by setting a non-standard value for
37 /// these variables.  Accordingly, the \c TfEnvSetting construct should be
38 /// used as sparingly as possible in code.
39 ///
40 /// In contrast, a variable that allows the user to customize program
41 /// execution but is not an in-development code path should simply use
42 /// TfGetenv() to access the variable.  An example would be supplying a
43 /// variable to override a default font or fontsize, for users who don't like
44 /// the default program choice (and when there is no other way to set the
45 /// preference).
46 ///
47 /// Here is how to use the TfEnvSetting facility.
48 ///
49 /// 1. First, define your variable in a single .cpp file:
50 ///
51 /// \code
52 /// #include "pxr/base/tf/envSetting.h"
53 ///
54 /// TF_DEFINE_ENV_SETTING(TDS_FILE_VERSION, 12,
55 ///                       "Default file format to use");
56 /// \endcode
57 ///
58 /// The first argument is the name of your variable; it is also the name for
59 /// the variable you can set in your shell to set the value at runtime.  The
60 /// second argument is the default value.  To create a bool variable, pass
61 /// either true or false.  To create a string variable, pass an explicit
62 /// string(), i.e.
63 ///
64 /// \code
65 /// TF_DEFINE_ENV_SETTING(TDS_FILE_SUFFIX, string(".tid"),
66 ///                       "Default file-name suffix");
67 /// \endcode
68 ///
69 /// 2.  If you need to access this variable outside the .cpp file that defines
70 /// the variable, put the following in a common header file:
71 ///
72 /// \code
73 /// extern TfEnvSetting<int> TDS_FILE_VERSION;
74 /// extern TfEnvSetting<string> TDS_FILE_SUFFIX;
75 /// \endcode
76 ///
77 /// 3. At runtime, access your variable using TfGetEnvSetting().  For example:
78 ///
79 /// \code
80 /// int version = TfGetEnvSetting(TDS_FILE_VERSION);
81 /// string const& suffix =TfGetEnvSetting(TDS_FILE_SUFFIX);
82 /// \endcode
83 ///
84 /// You can also access a variable's value from Python:
85 ///
86 /// \code{.py}
87 /// from pxr import Tf
88 /// suffix = Tf.GetEnvSetting("TDS_FILE_SUFFIX")
89 /// \endcode
90 ///
91 /// \c Tf.GetEnvSetting() returns the value for the TfEnvSetting variable, or
92 /// None if no such variable is defined in the currently loaded C++ code.
93 ///
94 /// If a user's environment has a value for a TfEnvSetting variable that
95 /// differs from the default, when the program starts or the module defining
96 /// the TfEnvSetting variable is loaded, a warning messages is printed.
97 ///
98 /// Additionally, at program startup time (or when lib/tf is first loaded),
99 /// the environment variable PIXAR_TF_ENV_SETTING_FILE is examined.  If this
100 /// variable indicates a file that can be read, then the file is parsed, and
101 /// should contain lines of the form key=value.  For each line read, the
102 /// environment variable key is set to value.  For example:
103 ///
104 /// \code{.sh}
105 /// $ setenv PIXAR_TF_ENV_SETTING_FILE /usr/anim/<UNIT>/admin/env-settings
106 ///
107 /// $ cat /usr/anim/<UNIT>/admin/env-settings
108 /// TDS_DEF_VERSION=30
109 /// TDS_BLAH=
110 /// TDS_LONG_STRING=i am some long string with spaces
111 /// \endcode
112 ///
113 /// Blank lines in the file and lines where the first character is '#' are
114 /// ignored.  If the file itself cannot be read, no error is printed; however,
115 /// if the file is malformed, errors are printed to stderr.
116 
117 #include "pxr/pxr.h"
118 #include "pxr/base/arch/hints.h"
119 #include "pxr/base/tf/registryManager.h"
120 
121 #include <atomic>
122 #include <string>
123 
124 PXR_NAMESPACE_OPEN_SCOPE
125 
126 // POD, statically initialized.
127 //
128 // We store the atomic_value separately and refer to it via pointer because we
129 // cannot use aggregate-initialization on a struct holding an atomic, but we
130 // can value-initialize a single std::atomic.
131 template <class T>
132 struct TfEnvSetting
133 {
134     std::atomic<T*> *_value;
135     T _default;
136     char const * _name;
137     char const * _description;
138 };
139 
140 // Specialize for string, default is stored as char const * (pointing to a
141 // literal).
142 template <>
143 struct TfEnvSetting<std::string>
144 {
145     std::atomic<std::string*> *_value;
146     char const * _default;
147     char const * _name;
148     char const * _description;
149 };
150 
151 template <class T>
152 void Tf_InitializeEnvSetting(TfEnvSetting<T> *);
153 
154 /// Returns the value of the specified env setting, registered using
155 /// \c TF_DEFINE_ENV_SETTING.
156 template <class T>
157 inline T const &
158 TfGetEnvSetting(TfEnvSetting<T>& setting) {
159     extern void Tf_InitEnvSettings();
160     Tf_InitEnvSettings();
161 
162     T *val = setting._value->load();
163     if (ARCH_UNLIKELY(!val)) {
164         Tf_InitializeEnvSetting(&setting);
165         val = setting._value->load();
166     }
167     return *val;
168 }
169 
170 // Ensure that we only allow bool, int, and string, and map char * and char
171 // array to string.
172 
173 bool Tf_ChooseEnvSettingType(bool);
174 int Tf_ChooseEnvSettingType(int);
175 std::string Tf_ChooseEnvSettingType(char const *);
176 
177 class Tf_EnvSettingRegistry;
178 
179 /// Define an env setting named \p envVar with default value \p defValue and a
180 /// descriptive string \p description.
181 /// \hideinitializer
182 #define TF_DEFINE_ENV_SETTING(envVar, defValue, description)                  \
183     std::atomic< decltype(Tf_ChooseEnvSettingType(defValue))*>                \
184         envVar##_value;                                                       \
185     TfEnvSetting<decltype(Tf_ChooseEnvSettingType(defValue))> envVar = {      \
186         &envVar##_value, defValue, #envVar, description };                    \
187     TF_REGISTRY_FUNCTION_WITH_TAG(Tf_EnvSettingRegistry, envVar) {            \
188         (void)TfGetEnvSetting(envVar);                                        \
189     }
190 
191 PXR_NAMESPACE_CLOSE_SCOPE
192 
193 #endif // PXR_BASE_TF_ENV_SETTING_H
194