about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--absl/flags/internal/flag.h64
-rw-r--r--absl/flags/internal/parse.h7
-rw-r--r--absl/flags/parse.cc40
-rw-r--r--absl/flags/parse_test.cc22
-rw-r--r--absl/strings/internal/str_format/arg.cc4
-rw-r--r--absl/strings/internal/str_format/arg.h21
-rw-r--r--absl/strings/internal/str_format/checker.h9
-rw-r--r--absl/strings/internal/str_format/extension.h17
8 files changed, 148 insertions, 36 deletions
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index f53f484f8f44..b060199e12b0 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -40,6 +40,30 @@
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
+
+// Forward declaration of absl::Flag<T> public API.
+namespace flags_internal {
+template <typename T>
+class Flag;
+}  // namespace flags_internal
+
+#if defined(_MSC_VER) && !defined(__clang__)
+template <typename T>
+class Flag;
+#else
+template <typename T>
+using Flag = flags_internal::Flag<T>;
+#endif
+
+template <typename T>
+ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag);
+
+template <typename T>
+void SetFlag(absl::Flag<T>* flag, const T& v);
+
+template <typename T, typename V>
+void SetFlag(absl::Flag<T>* flag, const V& v);
+
 namespace flags_internal {
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -596,6 +620,32 @@ class Flag {
               flags_internal::StorageKind<T>(), default_arg),
         value_() {}
 
+  // CommandLineFlag interface
+  absl::string_view Name() const { return impl_.Name(); }
+  std::string Filename() const { return impl_.Filename(); }
+  std::string Help() const { return impl_.Help(); }
+  bool IsSpecifiedOnCommandLine() const {
+    return impl_.IsSpecifiedOnCommandLine();
+  }
+  std::string DefaultValue() const { return impl_.DefaultValue(); }
+  std::string CurrentValue() const { return impl_.CurrentValue(); }
+
+ private:
+  template <typename U, bool do_register>
+  friend class FlagRegistrar;
+
+#if !defined(_MSC_VER) || defined(__clang__)
+  template <typename U>
+  friend U absl::GetFlag(const flags_internal::Flag<U>& flag);
+  template <typename U>
+  friend void absl::SetFlag(flags_internal::Flag<U>* flag, const U& v);
+  template <typename U, typename V>
+  friend void absl::SetFlag(flags_internal::Flag<U>* flag, const V& v);
+#else
+  template <typename U>
+  friend class absl::Flag;
+#endif
+
   T Get() const {
     // See implementation notes in CommandLineFlag::Get().
     union U {
@@ -617,20 +667,6 @@ class Flag {
     impl_.Write(&v);
   }
 
-  // CommandLineFlag interface
-  absl::string_view Name() const { return impl_.Name(); }
-  std::string Filename() const { return impl_.Filename(); }
-  std::string Help() const { return impl_.Help(); }
-  bool IsSpecifiedOnCommandLine() const {
-    return impl_.IsSpecifiedOnCommandLine();
-  }
-  std::string DefaultValue() const { return impl_.DefaultValue(); }
-  std::string CurrentValue() const { return impl_.CurrentValue(); }
-
- private:
-  template <typename U, bool do_register>
-  friend class FlagRegistrar;
-
   // Flag's data
   // The implementation depends on value_ field to be placed exactly after the
   // impl_ field, so that impl_ can figure out the offset to the value and
diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h
index 03e8a07bf3ab..d259be733ce2 100644
--- a/absl/flags/internal/parse.h
+++ b/absl/flags/internal/parse.h
@@ -44,6 +44,13 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
                                         UsageFlagsAction usage_flag_act,
                                         OnUndefinedFlag on_undef_flag);
 
+// --------------------------------------------------------------------
+// Inspect original command line
+
+// Returns true if flag with specified name was either present on the original
+// command line or specified in flag file present on the original command line.
+bool WasPresentOnCommandLine(absl::string_view flag_name);
+
 }  // namespace flags_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 66a28a93c7f7..2e8f03b4bb5d 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -67,6 +67,22 @@ ABSL_CONST_INIT bool fromenv_needs_processing
 ABSL_CONST_INIT bool tryfromenv_needs_processing
     ABSL_GUARDED_BY(processing_checks_guard) = false;
 
+ABSL_CONST_INIT absl::Mutex specified_flags_guard(absl::kConstInit);
+ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags
+    ABSL_GUARDED_BY(specified_flags_guard) = nullptr;
+
+struct SpecifiedFlagsCompare {
+  bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const {
+    return a->Name() < b->Name();
+  }
+  bool operator()(const CommandLineFlag* a, absl::string_view b) const {
+    return a->Name() < b;
+  }
+  bool operator()(absl::string_view a, const CommandLineFlag* b) const {
+    return a < b->Name();
+  }
+};
+
 }  // namespace
 }  // namespace flags_internal
 ABSL_NAMESPACE_END
@@ -577,6 +593,17 @@ bool CanIgnoreUndefinedFlag(absl::string_view flag_name) {
 
 // --------------------------------------------------------------------
 
+bool WasPresentOnCommandLine(absl::string_view flag_name) {
+  absl::MutexLock l(&specified_flags_guard);
+  ABSL_INTERNAL_CHECK(specified_flags != nullptr,
+                      "ParseCommandLine is not invoked yet");
+
+  return std::binary_search(specified_flags->begin(), specified_flags->end(),
+                            flag_name, SpecifiedFlagsCompare{});
+}
+
+// --------------------------------------------------------------------
+
 std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
                                         ArgvListAction arg_list_act,
                                         UsageFlagsAction usage_flag_act,
@@ -607,6 +634,13 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
   }
   output_args.push_back(argv[0]);
 
+  absl::MutexLock l(&specified_flags_guard);
+  if (specified_flags == nullptr) {
+    specified_flags = new std::vector<const CommandLineFlag*>;
+  } else {
+    specified_flags->clear();
+  }
+
   // Iterate through the list of the input arguments. First level are arguments
   // originated from argc/argv. Following levels are arguments originated from
   // recursive parsing of flagfile(s).
@@ -702,6 +736,8 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
             flag, value, SET_FLAGS_VALUE, kCommandLine, &error)) {
       flags_internal::ReportUsageError(error, true);
       success = false;
+    } else {
+      specified_flags->push_back(flag);
     }
   }
 
@@ -753,6 +789,10 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
     }
   }
 
+  // Trim and sort the vector.
+  specified_flags->shrink_to_fit();
+  std::sort(specified_flags->begin(), specified_flags->end(),
+            SpecifiedFlagsCompare{});
   return output_args;
 }
 
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 065f757a6c9e..e6a53ae6cb43 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -869,4 +869,26 @@ TEST_F(ParseDeathTest, TestHelpFlagHandling) {
   EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
 }
 
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, WasPresentOnCommandLine) {
+  const char* in_args1[] = {
+      "testbin",        "arg1", "--bool_flag",
+      "--int_flag=211", "arg2", "--double_flag=1.1",
+      "--string_flag",  "asd",  "--",
+      "--some_flag",    "arg4",
+  };
+
+  InvokeParse(in_args1);
+
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("bool_flag"));
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("int_flag"));
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("double_flag"));
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("string_flag"));
+  EXPECT_FALSE(flags::WasPresentOnCommandLine("some_flag"));
+  EXPECT_FALSE(flags::WasPresentOnCommandLine("another_flag"));
+}
+
+// --------------------------------------------------------------------
+
 }  // namespace
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 964f25f7da64..02646addec11 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -302,7 +302,7 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv,
       return ConvertFloatImpl(static_cast<double>(v), conv, sink);
 
     default:
-      return false;
+       ABSL_INTERNAL_ASSUME(false);
   }
 
   if (conv.is_basic()) {
@@ -321,7 +321,6 @@ bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv,
 
 inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
                              FormatSinkImpl *sink) {
-  if (conv.conversion_char() != FormatConversionCharInternal::s) return false;
   if (conv.is_basic()) {
     sink->Append(v);
     return true;
@@ -366,7 +365,6 @@ FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
 // ==================== Raw pointers ====================
 ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
     VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
-  if (conv.conversion_char() != FormatConversionCharInternal::p) return {false};
   if (!v.value) {
     sink->Append("(nil)");
     return {true};
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index 9a1e5ef276e8..8f79948bd91c 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -86,10 +86,6 @@ template <class AbslCord, typename std::enable_if<std::is_same<
 StringConvertResult FormatConvertImpl(const AbslCord& value,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink) {
-  if (conv.conversion_char() != FormatConversionCharInternal::s) {
-    return {false};
-  }
-
   bool is_left = conv.has_left_flag();
   size_t space_remaining = 0;
 
@@ -249,6 +245,15 @@ struct FormatArgImplFriend {
   }
 };
 
+template <typename Arg>
+constexpr FormatConversionCharSet ArgumentToConv() {
+  return absl::str_format_internal::ExtractCharSet(
+      decltype(str_format_internal::FormatConvertImpl(
+          std::declval<const Arg&>(),
+          std::declval<const FormatConversionSpecImpl&>(),
+          std::declval<FormatSinkImpl*>())){});
+}
+
 // A type-erased handle to a format argument.
 class FormatArgImpl {
  private:
@@ -411,9 +416,13 @@ class FormatArgImpl {
       return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
                       std::is_enum<T>());
     }
-
+    if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(),
+                                     spec.conversion_char()))) {
+      return false;
+    }
     return str_format_internal::FormatConvertImpl(
-               Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out))
+               Manager<T>::Value(arg), spec,
+               static_cast<FormatSinkImpl*>(out))
         .value;
   }
 
diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h
index 73ef05ff44b3..424c51f74f17 100644
--- a/absl/strings/internal/str_format/checker.h
+++ b/absl/strings/internal/str_format/checker.h
@@ -24,15 +24,6 @@ constexpr bool AllOf(bool b, T... t) {
   return b && AllOf(t...);
 }
 
-template <typename Arg>
-constexpr FormatConversionCharSet ArgumentToConv() {
-  return absl::str_format_internal::ExtractCharSet(
-      decltype(str_format_internal::FormatConvertImpl(
-          std::declval<const Arg&>(),
-          std::declval<const FormatConversionSpecImpl&>(),
-          std::declval<FormatSinkImpl*>())){});
-}
-
 #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
 constexpr bool ContainsChar(const char* chars, char c) {
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index ce78a02aefbb..36e70646d7d1 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -327,12 +327,16 @@ constexpr FormatConversionCharSet FormatConversionCharSetUnion(
       static_cast<uint64_t>(FormatConversionCharSetUnion(rest...)));
 }
 
+constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) {
+  return uint64_t{1} << (1 + static_cast<uint8_t>(c));
+}
+
 constexpr uint64_t FormatConversionCharToConvInt(char conv) {
   return
-#define ABSL_INTERNAL_CHAR_SET_CASE(c)                                        \
-  conv == #c[0] ? (uint64_t{1} << (1 + static_cast<uint8_t>(                  \
-                                           FormatConversionCharInternal::c))) \
-                :
+#define ABSL_INTERNAL_CHAR_SET_CASE(c)                                 \
+  conv == #c[0]                                                        \
+      ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \
+      :
       ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
 #undef ABSL_INTERNAL_CHAR_SET_CASE
                   conv == '*'
@@ -406,6 +410,11 @@ constexpr bool Contains(FormatConversionCharSet set,
          static_cast<uint64_t>(c);
 }
 
+// Checks whether all the characters in `c` are contained in `set`
+constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) {
+  return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0;
+}
+
 // Return capacity - used, clipped to a minimum of 0.
 inline size_t Excess(size_t used, size_t capacity) {
   return used < capacity ? capacity - used : 0;