about summary refs log tree commit diff
path: root/absl/flags
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags')
-rw-r--r--absl/flags/flag.h7
-rw-r--r--absl/flags/flag_test.cc5
-rw-r--r--absl/flags/internal/commandlineflag.h124
-rw-r--r--absl/flags/internal/flag.cc76
-rw-r--r--absl/flags/internal/flag.h113
-rw-r--r--absl/flags/internal/registry.cc12
-rw-r--r--absl/flags/internal/registry.h4
7 files changed, 165 insertions, 176 deletions
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 274838cbc7f0..cff02c1fcb61 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -100,12 +100,10 @@ class Flag {
   // constexpr initializable.
 #if _MSC_VER <= 1900
   constexpr Flag(const char* name, const char* filename,
-                 const flags_internal::FlagMarshallingOpFn marshalling_op,
                  const flags_internal::HelpGenFunc help_gen,
                  const flags_internal::FlagDfltGenFunc default_value_gen)
       : name_(name),
         filename_(filename),
-        marshalling_op_(marshalling_op),
         help_gen_(help_gen),
         default_value_gen_(default_value_gen),
         inited_(false),
@@ -121,7 +119,7 @@ class Flag {
       }
 
       impl_ =
-          new flags_internal::Flag<T>(name_, filename_, marshalling_op_,
+          new flags_internal::Flag<T>(name_, filename_,
                                       {flags_internal::FlagHelpMsg(help_gen_),
                                        flags_internal::FlagHelpKind::kGenFunc},
                                       default_value_gen_);
@@ -161,7 +159,6 @@ class Flag {
   // this to be an aggregate type.
   const char* name_;
   const char* filename_;
-  const flags_internal::FlagMarshallingOpFn marshalling_op_;
   const flags_internal::HelpGenFunc help_gen_;
   const flags_internal::FlagDfltGenFunc default_value_gen_;
 
@@ -335,7 +332,6 @@ ABSL_NAMESPACE_END
   ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help);                  \
   ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{                    \
       ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(),    \
-      &absl::flags_internal::FlagMarshallingOps<Type>,              \
       absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0),   \
       &AbslFlagsInitFlag##name};                                    \
   extern bool FLAGS_no##name;                                       \
@@ -349,7 +345,6 @@ ABSL_NAMESPACE_END
   ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help);                    \
   ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{                      \
       ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(),      \
-      &absl::flags_internal::FlagMarshallingOps<Type>,                \
       &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \
   extern bool FLAGS_no##name;                                         \
   bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 6429a3e1e1b3..4984d28403b2 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -53,14 +53,13 @@ template <typename T>
 bool TestConstructionFor() {
   constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
                                         flags::FlagHelpKind::kLiteral};
-  constexpr flags::Flag<T> f1("f1", "file", &flags::FlagMarshallingOps<T>,
-                              help_arg, &TestMakeDflt<T>);
+  constexpr flags::Flag<T> f1("f1", "file", help_arg, &TestMakeDflt<T>);
   EXPECT_EQ(f1.Name(), "f1");
   EXPECT_EQ(f1.Help(), "literal help");
   EXPECT_EQ(f1.Filename(), "file");
 
   ABSL_CONST_INIT static flags::Flag<T> f2(
-      "f2", "file", &flags::FlagMarshallingOps<T>,
+      "f2", "file",
       {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc},
       &TestMakeDflt<T>);
   flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback);
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 4ac50190c6c0..6363c6615b12 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -34,22 +34,23 @@ namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace flags_internal {
 
-// Type-specific operations, eg., parsing, copying, etc. are provided
-// by function specific to that type with a signature matching FlagOpFn.
-enum FlagOp {
-  kDelete,
-  kClone,
-  kCopy,
-  kCopyConstruct,
-  kSizeof,
-  kParse,
-  kUnparse,
+// An alias for flag static type id. Values of type identify the flag value type
+// simialarly to typeid(T), but without relying on RTTI being available. In most
+// cases this id is enough to uniquely identify the flag's value type. In a few
+// cases we'll have to resort to using actual RTTI implementation if it is
+// available.
+using FlagStaticTypeId = void* (*)();
+
+// Address of this function template is used in current implementation as a flag
+// static type id.
+template <typename T>
+void* FlagStaticTypeIdGen() {
 #if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
-  kRuntimeTypeId
+  return const_cast<std::type_info*>(&typeid(T));
+#else
+  return nullptr;
 #endif
-};
-using FlagOpFn = void* (*)(FlagOp, const void*, void*);
-using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*);
+}
 
 // Options that control SetCommandLineOptionWithMode.
 enum FlagSettingMode {
@@ -72,97 +73,6 @@ enum ValueSource {
   kProgrammaticChange,
 };
 
-// The per-type function
-template <typename T>
-void* FlagOps(FlagOp op, const void* v1, void* v2) {
-  switch (op) {
-    case kDelete:
-      delete static_cast<const T*>(v1);
-      return nullptr;
-    case kClone:
-      return new T(*static_cast<const T*>(v1));
-    case kCopy:
-      *static_cast<T*>(v2) = *static_cast<const T*>(v1);
-      return nullptr;
-    case kCopyConstruct:
-      new (v2) T(*static_cast<const T*>(v1));
-      return nullptr;
-    case kSizeof:
-      return reinterpret_cast<void*>(sizeof(T));
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
-    case kRuntimeTypeId:
-      return const_cast<std::type_info*>(&typeid(T));
-      break;
-#endif
-    default:
-      return nullptr;
-  }
-}
-
-template <typename T>
-void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) {
-  switch (op) {
-    case kParse: {
-      // initialize the temporary instance of type T based on current value in
-      // destination (which is going to be flag's default value).
-      T temp(*static_cast<T*>(v2));
-      if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
-                              static_cast<std::string*>(v3))) {
-        return nullptr;
-      }
-      *static_cast<T*>(v2) = std::move(temp);
-      return v2;
-    }
-    case kUnparse:
-      *static_cast<std::string*>(v2) =
-          absl::UnparseFlag<T>(*static_cast<const T*>(v1));
-      return nullptr;
-    default:
-      return nullptr;
-  }
-}
-
-// Functions that invoke flag-type-specific operations.
-inline void Delete(FlagOpFn op, const void* obj) {
-  op(flags_internal::kDelete, obj, nullptr);
-}
-
-inline void* Clone(FlagOpFn op, const void* obj) {
-  return op(flags_internal::kClone, obj, nullptr);
-}
-
-inline void Copy(FlagOpFn op, const void* src, void* dst) {
-  op(flags_internal::kCopy, src, dst);
-}
-
-inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
-  op(flags_internal::kCopyConstruct, src, dst);
-}
-
-inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst,
-                  std::string* error) {
-  return op(flags_internal::kParse, &text, dst, error) != nullptr;
-}
-
-inline std::string Unparse(FlagMarshallingOpFn op, const void* val) {
-  std::string result;
-  op(flags_internal::kUnparse, val, &result, nullptr);
-  return result;
-}
-
-inline size_t Sizeof(FlagOpFn op) {
-  // This sequence of casts reverses the sequence from base::internal::FlagOps()
-  return static_cast<size_t>(reinterpret_cast<intptr_t>(
-      op(flags_internal::kSizeof, nullptr, nullptr)));
-}
-
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
-inline const std::type_info& RuntimeTypeId(FlagOpFn op) {
-  return *static_cast<const std::type_info*>(
-      op(flags_internal::kRuntimeTypeId, nullptr, nullptr));
-}
-#endif
-
 // Handle to FlagState objects. Specific flag state objects will restore state
 // of a flag produced this flag state from method CommandLineFlag::SaveState().
 class FlagStateInterface {
@@ -187,7 +97,7 @@ class CommandLineFlag {
   // Return true iff flag has type T.
   template <typename T>
   inline bool IsOfType() const {
-    return TypeId() == &flags_internal::FlagOps<T>;
+    return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>;
   }
 
   // Attempts to retrieve the flag value. Returns value on success,
@@ -240,7 +150,7 @@ class CommandLineFlag {
   // Returns true iff this is a handle to an Abseil Flag.
   virtual bool IsAbseilFlag() const { return true; }
   // Returns id of the flag's value type.
-  virtual flags_internal::FlagOpFn TypeId() const = 0;
+  virtual FlagStaticTypeId TypeId() const = 0;
   virtual bool IsModified() const = 0;
   virtual bool IsSpecifiedOnCommandLine() const = 0;
   virtual std::string DefaultValue() const = 0;
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index 721e411e0875..83ec8df19149 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -47,23 +47,15 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
 namespace {
 
 // Currently we only validate flag values for user-defined flag types.
-bool ShouldValidateFlagValue(FlagOpFn flag_type_id) {
+bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) {
 #define DONT_VALIDATE(T) \
-  if (flag_type_id == &flags_internal::FlagOps<T>) return false;
+  if (flag_type_id == &FlagStaticTypeIdGen<T>) return false;
   ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE)
 #undef DONT_VALIDATE
 
   return true;
 }
 
-#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
-bool MatchRuntimeTypeId(FlagOpFn lhs_type_id, FlagOpFn rhs_type_id) {
-  return RuntimeTypeId(lhs_type_id) == RuntimeTypeId(rhs_type_id);
-}
-#else
-bool MatchRuntimeTypeId(FlagOpFn, FlagOpFn) { return true; }
-#endif
-
 // RAII helper used to temporarily unlock and relock `absl::Mutex`.
 // This is used when we need to ensure that locks are released while
 // invoking user supplied callbacks and then reacquired, since callbacks may
@@ -101,22 +93,35 @@ absl::Mutex* FlagImpl::DataGuard() const {
   return reinterpret_cast<absl::Mutex*>(&data_guard_);
 }
 
-void FlagImpl::AssertValidType(const flags_internal::FlagOpFn op) const {
-  // `op` is the unmarshaling operation corresponding to the declaration
-  // visibile at the call site. `op_` is the Flag's defined unmarshalling
-  // operation. They must match for this operation to be well-defined.
-  if (ABSL_PREDICT_FALSE(op != op_) && !MatchRuntimeTypeId(op, op_)) {
-    ABSL_INTERNAL_LOG(
-        FATAL,
-        absl::StrCat("Flag '", Name(),
-                     "' is defined as one type and declared as another"));
-  }
+void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const {
+  FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_);
+
+  // `type_id` is the type id corresponding to the declaration visibile at the
+  // call site. `this_type_id` is the type id corresponding to the type stored
+  // during flag definition. They must match for this operation to be
+  // well-defined.
+  if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return;
+
+  void* lhs_runtime_type_id = type_id();
+  void* rhs_runtime_type_id = this_type_id();
+
+  if (lhs_runtime_type_id == rhs_runtime_type_id) return;
+
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+  if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) ==
+      *reinterpret_cast<std::type_info*>(rhs_runtime_type_id))
+    return;
+#endif
+
+  ABSL_INTERNAL_LOG(
+      FATAL, absl::StrCat("Flag '", Name(),
+                          "' is defined as one type and declared as another"));
 }
 
 std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
   void* res = nullptr;
   if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
-    res = Clone(op_, default_src_.dynamic_value);
+    res = flags_internal::Clone(op_, default_src_.dynamic_value);
   } else {
     res = (*default_src_.gen_func)();
   }
@@ -148,13 +153,13 @@ std::string FlagImpl::DefaultValue() const {
   absl::MutexLock l(DataGuard());
 
   auto obj = MakeInitValue();
-  return Unparse(marshalling_op_, obj.get());
+  return flags_internal::Unparse(op_, obj.get());
 }
 
 std::string FlagImpl::CurrentValue() const {
   absl::MutexLock l(DataGuard());
 
-  return Unparse(marshalling_op_, value_.dynamic);
+  return flags_internal::Unparse(op_, value_.dynamic);
 }
 
 void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
@@ -220,7 +225,7 @@ bool FlagImpl::TryParse(void** dst, absl::string_view value,
   auto tentative_value = MakeInitValue();
 
   std::string parse_err;
-  if (!Parse(marshalling_op_, value, tentative_value.get(), &parse_err)) {
+  if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
     absl::string_view err_sep = parse_err.empty() ? "" : "; ";
     *err = absl::StrCat("Illegal value '", value, "' specified for flag '",
                         Name(), "'", err_sep, parse_err);
@@ -237,11 +242,11 @@ bool FlagImpl::TryParse(void** dst, absl::string_view value,
 void FlagImpl::Read(void* dst) const {
   absl::ReaderMutexLock l(DataGuard());
 
-  CopyConstruct(op_, value_.dynamic, dst);
+  flags_internal::CopyConstruct(op_, value_.dynamic, dst);
 }
 
 void FlagImpl::StoreAtomic() {
-  size_t data_size = Sizeof(op_);
+  size_t data_size = flags_internal::Sizeof(op_);
 
   if (data_size <= sizeof(int64_t)) {
     int64_t t = 0;
@@ -260,20 +265,20 @@ void FlagImpl::StoreAtomic() {
 void FlagImpl::Write(const void* src) {
   absl::MutexLock l(DataGuard());
 
-  if (ShouldValidateFlagValue(op_)) {
-    void* obj = Clone(op_, src);
+  if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) {
+    void* obj = flags_internal::Clone(op_, src);
     std::string ignored_error;
-    std::string src_as_str = Unparse(marshalling_op_, src);
-    if (!Parse(marshalling_op_, src_as_str, obj, &ignored_error)) {
+    std::string src_as_str = flags_internal::Unparse(op_, src);
+    if (!flags_internal::Parse(op_, src_as_str, obj, &ignored_error)) {
       ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
                                             "' to invalid value ", src_as_str));
     }
-    Delete(op_, obj);
+    flags_internal::Delete(op_, obj);
   }
 
   modified_ = true;
   counter_++;
-  Copy(op_, src, value_.dynamic);
+  flags_internal::Copy(op_, src, value_.dynamic);
 
   StoreAtomic();
   InvokeCallback();
@@ -341,7 +346,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode,
 
       if (!modified_) {
         // Need to set both default value *and* current, in this case
-        Copy(op_, default_src_.dynamic_value, value_.dynamic);
+        flags_internal::Copy(op_, default_src_.dynamic_value, value_.dynamic);
         StoreAtomic();
         InvokeCallback();
       }
@@ -359,7 +364,7 @@ void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
 
   auto dst = MakeInitValue();
   std::string error;
-  if (!flags_internal::Parse(marshalling_op_, v, dst.get(), &error)) {
+  if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
     ABSL_INTERNAL_LOG(
         FATAL,
         absl::StrCat("Flag ", Name(), " (from ", Filename(),
@@ -376,8 +381,7 @@ bool FlagImpl::ValidateInputValue(absl::string_view value) const {
 
   auto obj = MakeInitValue();
   std::string ignored_error;
-  return flags_internal::Parse(marshalling_op_, value, obj.get(),
-                               &ignored_error);
+  return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
 }
 
 }  // namespace flags_internal
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index b426ccb571a6..1c2f15dd7388 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -43,6 +43,94 @@ template <typename T>
 class Flag;
 
 ///////////////////////////////////////////////////////////////////////////////
+// Type-specific operations, eg., parsing, copying, etc. are provided
+// by function specific to that type with a signature matching FlagOpFn.
+
+enum FlagOp {
+  kDelete,
+  kClone,
+  kCopy,
+  kCopyConstruct,
+  kSizeof,
+  kStaticTypeId,
+  kParse,
+  kUnparse,
+};
+using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*);
+
+// The per-type function
+template <typename T>
+void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
+  switch (op) {
+    case flags_internal::kDelete:
+      delete static_cast<const T*>(v1);
+      return nullptr;
+    case flags_internal::kClone:
+      return new T(*static_cast<const T*>(v1));
+    case flags_internal::kCopy:
+      *static_cast<T*>(v2) = *static_cast<const T*>(v1);
+      return nullptr;
+    case flags_internal::kCopyConstruct:
+      new (v2) T(*static_cast<const T*>(v1));
+      return nullptr;
+    case flags_internal::kSizeof:
+      return reinterpret_cast<void*>(sizeof(T));
+    case flags_internal::kStaticTypeId:
+      return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>);
+    case flags_internal::kParse: {
+      // Initialize the temporary instance of type T based on current value in
+      // destination (which is going to be flag's default value).
+      T temp(*static_cast<T*>(v2));
+      if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
+                              static_cast<std::string*>(v3))) {
+        return nullptr;
+      }
+      *static_cast<T*>(v2) = std::move(temp);
+      return v2;
+    }
+    case flags_internal::kUnparse:
+      *static_cast<std::string*>(v2) =
+          absl::UnparseFlag<T>(*static_cast<const T*>(v1));
+      return nullptr;
+    default:
+      return nullptr;
+  }
+}
+
+// Functions that invoke flag-type-specific operations.
+inline void Delete(FlagOpFn op, const void* obj) {
+  op(flags_internal::kDelete, obj, nullptr, nullptr);
+}
+inline void* Clone(FlagOpFn op, const void* obj) {
+  return op(flags_internal::kClone, obj, nullptr, nullptr);
+}
+inline void Copy(FlagOpFn op, const void* src, void* dst) {
+  op(flags_internal::kCopy, src, dst, nullptr);
+}
+inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
+  op(flags_internal::kCopyConstruct, src, dst, nullptr);
+}
+inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
+                  std::string* error) {
+  return op(flags_internal::kParse, &text, dst, error) != nullptr;
+}
+inline std::string Unparse(FlagOpFn op, const void* val) {
+  std::string result;
+  op(flags_internal::kUnparse, val, &result, nullptr);
+  return result;
+}
+inline size_t Sizeof(FlagOpFn op) {
+  // This sequence of casts reverses the sequence from
+  // `flags_internal::FlagOps()`
+  return static_cast<size_t>(reinterpret_cast<intptr_t>(
+      op(flags_internal::kSizeof, nullptr, nullptr, nullptr)));
+}
+inline FlagStaticTypeId StaticTypeId(FlagOpFn op) {
+  return reinterpret_cast<FlagStaticTypeId>(
+      op(flags_internal::kStaticTypeId, nullptr, nullptr, nullptr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Persistent state of the flag data.
 
 template <typename T>
@@ -273,12 +361,10 @@ struct DynValueDeleter {
 class FlagImpl {
  public:
   constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
-                     FlagMarshallingOpFn marshalling_op, FlagHelpArg help,
-                     FlagDfltGenFunc default_value_gen)
+                     FlagHelpArg help, FlagDfltGenFunc default_value_gen)
       : name_(name),
         filename_(filename),
         op_(op),
-        marshalling_op_(marshalling_op),
         help_(help.source),
         help_source_kind_(static_cast<uint8_t>(help.kind)),
         def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)),
@@ -306,7 +392,7 @@ class FlagImpl {
   template <typename T, typename std::enable_if<
                             !IsAtomicFlagTypeTrait<T>::value, int>::type = 0>
   void Get(T* dst) const {
-    AssertValidType(&flags_internal::FlagOps<T>);
+    AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
     Read(dst);
   }
   // Overload for `GetFlag()` for types that support lock-free reads.
@@ -317,7 +403,7 @@ class FlagImpl {
     // slowing down flag value access due to type validation. That's why
     // this validation is hidden behind !NDEBUG
 #ifndef NDEBUG
-    AssertValidType(&flags_internal::FlagOps<T>);
+    AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
 #endif
     using U = flags_internal::BestAtomicType<T>;
     typename U::type r = value_.atomics.template load<T>();
@@ -329,7 +415,7 @@ class FlagImpl {
   }
   template <typename T>
   void Set(const T& src) {
-    AssertValidType(&flags_internal::FlagOps<T>);
+    AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
     Write(&src);
   }
 
@@ -388,7 +474,7 @@ class FlagImpl {
   // int. To do that we pass the "assumed" type id (which is deduced from type
   // int) as an argument `op`, which is in turn is validated against the type id
   // stored in flag object by flag definition statement.
-  void AssertValidType(const flags_internal::FlagOpFn op) const;
+  void AssertValidType(FlagStaticTypeId type_id) const;
 
   // Immutable flag's state.
 
@@ -396,10 +482,8 @@ class FlagImpl {
   const char* const name_;
   // The file name where ABSL_FLAG resides.
   const char* const filename_;
-  // Type-specific handler.
+  // Type-specific operations "vtable".
   const FlagOpFn op_;
-  // Marshalling ops handler.
-  const FlagMarshallingOpFn marshalling_op_;
   // Help message literal or function to generate it.
   const FlagHelpMsg help_;
   // Indicates if help message was supplied as literal or generator func.
@@ -456,12 +540,9 @@ class FlagImpl {
 template <typename T>
 class Flag final : public flags_internal::CommandLineFlag {
  public:
-  constexpr Flag(const char* name, const char* filename,
-                 const FlagMarshallingOpFn marshalling_op,
-                 const FlagHelpArg help,
+  constexpr Flag(const char* name, const char* filename, const FlagHelpArg help,
                  const FlagDfltGenFunc default_value_gen)
-      : impl_(name, filename, &FlagOps<T>, marshalling_op, help,
-              default_value_gen) {}
+      : impl_(name, filename, &FlagOps<T>, help, default_value_gen) {}
 
   T Get() const {
     // See implementation notes in CommandLineFlag::Get().
@@ -520,7 +601,7 @@ class Flag final : public flags_internal::CommandLineFlag {
   friend class FlagState<T>;
 
   void Read(void* dst) const override { impl_.Read(dst); }
-  FlagOpFn TypeId() const override { return &FlagOps<T>; }
+  FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; }
 
   // Flag's data
   FlagImpl impl_;
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index 2ef16e84864c..e434a8591224 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -284,14 +284,14 @@ namespace {
 
 class RetiredFlagObj final : public flags_internal::CommandLineFlag {
  public:
-  constexpr RetiredFlagObj(const char* name, FlagOpFn ops)
-      : name_(name), op_(ops) {}
+  constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id)
+      : name_(name), type_id_(type_id) {}
 
  private:
   absl::string_view Name() const override { return name_; }
   std::string Filename() const override { return "RETIRED"; }
   absl::string_view Typename() const override { return ""; }
-  flags_internal::FlagOpFn TypeId() const override { return op_; }
+  FlagStaticTypeId TypeId() const override { return type_id_; }
   std::string Help() const override { return ""; }
   bool IsRetired() const override { return true; }
   bool IsModified() const override { return false; }
@@ -317,7 +317,7 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag {
 
   // Data members
   const char* const name_;
-  const FlagOpFn op_;
+  const FlagStaticTypeId type_id_;
 };
 
 void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
@@ -327,8 +327,8 @@ void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
 
 }  // namespace
 
-bool Retire(const char* name, FlagOpFn ops) {
-  auto* flag = new flags_internal::RetiredFlagObj(name, ops);
+bool Retire(const char* name, FlagStaticTypeId type_id) {
+  auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
   FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
   return true;
 }
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index 99cb685b15d4..69ff889fb100 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -79,12 +79,12 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
 //
 
 // Retire flag with name "name" and type indicated by ops.
-bool Retire(const char* name, FlagOpFn ops);
+bool Retire(const char* name, FlagStaticTypeId type_id);
 
 // Registered a retired flag with name 'flag_name' and type 'T'.
 template <typename T>
 inline bool RetiredFlag(const char* flag_name) {
-  return flags_internal::Retire(flag_name, flags_internal::FlagOps<T>);
+  return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>);
 }
 
 // If the flag is retired, returns true and indicates in |*type_is_bool|