xref: /llvm-project/offload/include/Shared/EnvironmentVar.h (revision 9a1013220b668d846e63f241203b80515dee0a03)
1 //===-- Shared/EnvironmentVar.h - Environment variable handling -*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #ifndef OMPTARGET_SHARED_ENVIRONMENT_VAR_H
12 #define OMPTARGET_SHARED_ENVIRONMENT_VAR_H
13 
14 #include "Debug.h"
15 
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Error.h"
18 
19 #include <sstream>
20 #include <string>
21 
22 /// Utility class for parsing strings to other types.
23 struct StringParser {
24   /// Parse a string to another type.
25   template <typename Ty> static bool parse(const char *Value, Ty &Result);
26 };
27 
28 /// Class for reading and checking environment variables. Currently working with
29 /// integer, floats, std::string and bool types.
30 template <typename Ty> class Envar {
31   llvm::StringRef Name;
32   Ty Data;
33   bool IsPresent;
34   bool Initialized;
35 
36 public:
37   /// Auxiliary function to safely create envars. This static function safely
38   /// creates envars using fallible constructors. See the constructors to know
39   /// more details about the creation parameters.
40   template <typename... ArgsTy>
41   static llvm::Expected<Envar> create(ArgsTy &&...Args) {
42     llvm::Error Err = llvm::Error::success();
43     Envar Envar(std::forward<ArgsTy>(Args)..., Err);
44     if (Err)
45       return std::move(Err);
46     return std::move(Envar);
47   }
48 
49   /// Create an empty envar. Cannot be consulted. This constructor is merely
50   /// for convenience. This constructor is not fallible.
51   Envar() : Data(Ty()), IsPresent(false), Initialized(false) {}
52 
53   /// Create an envar with a name and an optional default. The Envar object will
54   /// take the value read from the environment variable, or the default if it
55   /// was not set or not correct. This constructor is not fallible.
56   Envar(llvm::StringRef Name, Ty Default = Ty())
57       : Name(Name), Data(Default), IsPresent(false), Initialized(true) {
58 
59     if (const char *EnvStr = getenv(Name.data())) {
60       // Check whether the envar is defined and valid.
61       IsPresent = StringParser::parse<Ty>(EnvStr, Data);
62 
63       if (!IsPresent) {
64         DP("Ignoring invalid value %s for envar %s\n", EnvStr, Name.data());
65         Data = Default;
66       }
67     }
68   }
69 
70   Envar<Ty> &operator=(const Ty &V) {
71     Data = V;
72     Initialized = true;
73     return *this;
74   }
75 
76   /// Get the definitive value.
77   const Ty &get() const {
78     // Throw a runtime error in case this envar is not initialized.
79     if (!Initialized)
80       FATAL_MESSAGE0(1, "Consulting envar before initialization");
81 
82     return Data;
83   }
84 
85   /// Get the definitive value.
86   operator Ty() const { return get(); }
87 
88   /// Return the environment variable name.
89   llvm::StringRef getName() const { return Name; }
90 
91   /// Indicate whether the environment variable was defined and valid.
92   bool isPresent() const { return IsPresent; }
93 
94 private:
95   /// This constructor should never fail but we provide it for convenience. This
96   /// way, the constructor can be used by the Envar::create() static function
97   /// to safely create this kind of envars.
98   Envar(llvm::StringRef Name, Ty Default, llvm::Error &Err)
99       : Envar(Name, Default) {
100     llvm::ErrorAsOutParameter EAO(&Err);
101     Err = llvm::Error::success();
102   }
103 
104   /// Create an envar with a name, getter function and a setter function. The
105   /// Envar object will take the value read from the environment variable if
106   /// this value is accepted by the setter function. Otherwise, the getter
107   /// function will be executed to get the default value. The getter should be
108   /// of the form Error GetterFunctionTy(Ty &Value) and the setter should
109   /// be of the form Error SetterFunctionTy(Ty Value). This constructor has a
110   /// private visibility because is a fallible constructor. Please use the
111   /// Envar::create() static function to safely create this object instead.
112   template <typename GetterFunctor, typename SetterFunctor>
113   Envar(llvm::StringRef Name, GetterFunctor Getter, SetterFunctor Setter,
114         llvm::Error &Err)
115       : Data(Ty()), IsPresent(false), Initialized(true) {
116     llvm::ErrorAsOutParameter EAO(&Err);
117     Err = init(Name, Getter, Setter);
118   }
119 
120   template <typename GetterFunctor, typename SetterFunctor>
121   llvm::Error init(llvm::StringRef Name, GetterFunctor Getter,
122                    SetterFunctor Setter);
123 };
124 
125 /// Define some common envar types.
126 using IntEnvar = Envar<int>;
127 using Int32Envar = Envar<int32_t>;
128 using Int64Envar = Envar<int64_t>;
129 using UInt32Envar = Envar<uint32_t>;
130 using UInt64Envar = Envar<uint64_t>;
131 using StringEnvar = Envar<std::string>;
132 using BoolEnvar = Envar<bool>;
133 
134 template <>
135 inline bool StringParser::parse(const char *ValueStr, bool &Result) {
136   std::string Value(ValueStr);
137 
138   // Convert the string to lowercase.
139   std::transform(Value.begin(), Value.end(), Value.begin(),
140                  [](unsigned char c) { return std::tolower(c); });
141 
142   // May be implemented with fancier C++ features, but let's keep it simple.
143   if (Value == "true" || Value == "yes" || Value == "on" || Value == "1")
144     Result = true;
145   else if (Value == "false" || Value == "no" || Value == "off" || Value == "0")
146     Result = false;
147   else
148     return false;
149 
150   // Parsed correctly.
151   return true;
152 }
153 
154 template <typename Ty>
155 inline bool StringParser::parse(const char *Value, Ty &Result) {
156   assert(Value && "Parsed value cannot be null");
157 
158   std::istringstream Stream(Value);
159   Stream >> Result;
160 
161   return !Stream.fail();
162 }
163 
164 template <typename Ty>
165 template <typename GetterFunctor, typename SetterFunctor>
166 inline llvm::Error Envar<Ty>::init(llvm::StringRef Name, GetterFunctor Getter,
167                                    SetterFunctor Setter) {
168   // Get the default value.
169   Ty Default;
170   if (llvm::Error Err = Getter(Default))
171     return Err;
172 
173   if (const char *EnvStr = getenv(Name.data())) {
174     IsPresent = StringParser::parse<Ty>(EnvStr, Data);
175     if (IsPresent) {
176       // Check whether the envar value is actually valid.
177       llvm::Error Err = Setter(Data);
178       if (Err) {
179         // The setter reported an invalid value. Mark the user-defined value as
180         // not present and reset to the getter value (default).
181         IsPresent = false;
182         Data = Default;
183         DP("Setter of envar %s failed, resetting to %s\n", Name.data(),
184            std::to_string(Data).data());
185         consumeError(std::move(Err));
186       }
187     } else {
188       DP("Ignoring invalid value %s for envar %s\n", EnvStr, Name.data());
189       Data = Default;
190     }
191   } else {
192     Data = Default;
193   }
194 
195   return llvm::Error::success();
196 }
197 
198 #endif // OMPTARGET_SHARED_ENVIRONMENT_VAR_H
199