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