199 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8//9//===----------------------------------------------------------------------===//10 11#ifndef OMPTARGET_SHARED_ENVIRONMENT_VAR_H12#define OMPTARGET_SHARED_ENVIRONMENT_VAR_H13 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.23struct 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 with29/// integer, floats, std::string and bool types.30template <typename Ty> class Envar {31 llvm::StringRef Name;32 Ty Data;33 bool IsPresent;34 bool Initialized;35 36public:37 /// Auxiliary function to safely create envars. This static function safely38 /// creates envars using fallible constructors. See the constructors to know39 /// 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 merely50 /// 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 will54 /// take the value read from the environment variable, or the default if it55 /// 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 94private:95 /// This constructor should never fail but we provide it for convenience. This96 /// way, the constructor can be used by the Envar::create() static function97 /// 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. The105 /// Envar object will take the value read from the environment variable if106 /// this value is accepted by the setter function. Otherwise, the getter107 /// function will be executed to get the default value. The getter should be108 /// of the form Error GetterFunctionTy(Ty &Value) and the setter should109 /// be of the form Error SetterFunctionTy(Ty Value). This constructor has a110 /// private visibility because is a fallible constructor. Please use the111 /// 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.126using IntEnvar = Envar<int>;127using Int32Envar = Envar<int32_t>;128using Int64Envar = Envar<int64_t>;129using UInt32Envar = Envar<uint32_t>;130using UInt64Envar = Envar<uint64_t>;131using StringEnvar = Envar<std::string>;132using BoolEnvar = Envar<bool>;133 134template <>135inline 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 else148 return false;149 150 // Parsed correctly.151 return true;152}153 154template <typename Ty>155inline 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 164template <typename Ty>165template <typename GetterFunctor, typename SetterFunctor>166inline 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 as180 // 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_H199