aboutsummaryrefslogtreecommitdiff
path: root/absl/flags
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags')
-rw-r--r--absl/flags/BUILD.bazel2
-rw-r--r--absl/flags/CMakeLists.txt1
-rw-r--r--absl/flags/flag.h6
-rw-r--r--absl/flags/flag_test.cc173
-rw-r--r--absl/flags/internal/flag.cc73
-rw-r--r--absl/flags/internal/flag.h103
6 files changed, 248 insertions, 110 deletions
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 4b51d9d4..685e3954 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -326,7 +326,9 @@ cc_test(
":handle",
":registry",
"//absl/base:core_headers",
+ "//absl/base:malloc_internal",
"//absl/strings",
+ "//absl/time",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index 2204b0ff..ec82ee1e 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -304,6 +304,7 @@ absl_cc_test(
absl::flags_internal
absl::flags_registry
absl::strings
+ absl::time
gtest_main
)
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 4cc8ae37..15061592 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -314,9 +314,9 @@ ABSL_NAMESPACE_END
static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \
}
-#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
- static void* AbslFlagsInitFlag##name() { \
- return absl::flags_internal::MakeFromDefaultValue<Type>(default_value); \
+#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
+ static void AbslFlagsInitFlag##name(void* dst) { \
+ absl::flags_internal::MakeFromDefaultValue<Type>(dst, default_value); \
}
// ABSL_FLAG_IMPL
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 377e3b2b..dbe94a2c 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -19,6 +19,7 @@
#include <cmath>
#include <string>
+#include <thread> // NOLINT
#include <vector>
#include "gtest/gtest.h"
@@ -34,6 +35,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
+#include "absl/time/time.h"
ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag);
ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag);
@@ -44,8 +46,8 @@ namespace flags = absl::flags_internal;
std::string TestHelpMsg() { return "dynamic help"; }
template <typename T>
-void* TestMakeDflt() {
- return new T{};
+void TestMakeDflt(void* dst) {
+ new (dst) T{};
}
void TestCallback() {}
@@ -74,6 +76,7 @@ class FlagTest : public testing::Test {
#endif
return std::string(fname);
}
+ flags::FlagSaver flag_saver_;
};
struct S1 {
@@ -107,15 +110,15 @@ TEST_F(FlagTest, Traits) {
flags::FlagValueStorageKind::kTwoWordsAtomic);
#else
EXPECT_EQ(flags::StorageKind<S1>(),
- flags::FlagValueStorageKind::kHeapAllocated);
+ flags::FlagValueStorageKind::kAlignedBuffer);
EXPECT_EQ(flags::StorageKind<S2>(),
- flags::FlagValueStorageKind::kHeapAllocated);
+ flags::FlagValueStorageKind::kAlignedBuffer);
#endif
EXPECT_EQ(flags::StorageKind<std::string>(),
- flags::FlagValueStorageKind::kHeapAllocated);
+ flags::FlagValueStorageKind::kAlignedBuffer);
EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(),
- flags::FlagValueStorageKind::kHeapAllocated);
+ flags::FlagValueStorageKind::kAlignedBuffer);
}
// --------------------------------------------------------------------
@@ -190,6 +193,7 @@ ABSL_DECLARE_FLAG(uint64_t, test_flag_08);
ABSL_DECLARE_FLAG(double, test_flag_09);
ABSL_DECLARE_FLAG(float, test_flag_10);
ABSL_DECLARE_FLAG(std::string, test_flag_11);
+ABSL_DECLARE_FLAG(absl::Duration, test_flag_12);
namespace {
@@ -208,6 +212,7 @@ TEST_F(FlagTest, TestFlagDeclaration) {
EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09");
EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10");
EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11");
+ EXPECT_EQ(FLAGS_test_flag_12.Name(), "test_flag_12");
}
#endif // !ABSL_FLAGS_STRIP_NAMES
@@ -226,6 +231,7 @@ ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08");
ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09");
ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10");
ABSL_FLAG(std::string, test_flag_11, "", "test flag 11");
+ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "test flag 12");
namespace {
@@ -287,6 +293,11 @@ TEST_F(FlagTest, TestFlagDefinition) {
EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11");
EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name))
<< FLAGS_test_flag_11.Filename();
+
+ EXPECT_EQ(FLAGS_test_flag_12.Name(), "test_flag_12");
+ EXPECT_EQ(FLAGS_test_flag_12.Help(), "test flag 12");
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_12.Filename(), expected_file_name))
+ << FLAGS_test_flag_12.Filename();
}
#endif // !ABSL_FLAGS_STRIP_NAMES
@@ -304,6 +315,20 @@ TEST_F(FlagTest, TestDefault) {
EXPECT_EQ(FLAGS_test_flag_09.DefaultValue(), "-9.876e-50");
EXPECT_EQ(FLAGS_test_flag_10.DefaultValue(), "1.234e+12");
EXPECT_EQ(FLAGS_test_flag_11.DefaultValue(), "");
+ EXPECT_EQ(FLAGS_test_flag_12.DefaultValue(), "10m");
+
+ EXPECT_EQ(FLAGS_test_flag_01.CurrentValue(), "true");
+ EXPECT_EQ(FLAGS_test_flag_02.CurrentValue(), "1234");
+ EXPECT_EQ(FLAGS_test_flag_03.CurrentValue(), "-34");
+ EXPECT_EQ(FLAGS_test_flag_04.CurrentValue(), "189");
+ EXPECT_EQ(FLAGS_test_flag_05.CurrentValue(), "10765");
+ EXPECT_EQ(FLAGS_test_flag_06.CurrentValue(), "40000");
+ EXPECT_EQ(FLAGS_test_flag_07.CurrentValue(), "-1234567");
+ EXPECT_EQ(FLAGS_test_flag_08.CurrentValue(), "9876543");
+ EXPECT_EQ(FLAGS_test_flag_09.CurrentValue(), "-9.876e-50");
+ EXPECT_EQ(FLAGS_test_flag_10.CurrentValue(), "1.234e+12");
+ EXPECT_EQ(FLAGS_test_flag_11.CurrentValue(), "");
+ EXPECT_EQ(FLAGS_test_flag_12.CurrentValue(), "10m");
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
@@ -316,6 +341,7 @@ TEST_F(FlagTest, TestDefault) {
EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55);
EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "");
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10));
}
// --------------------------------------------------------------------
@@ -408,6 +434,38 @@ TEST_F(FlagTest, TestGetSet) {
absl::SetFlag(&FLAGS_test_flag_11, "asdf");
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf");
+
+ absl::SetFlag(&FLAGS_test_flag_12, absl::Seconds(110));
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Seconds(110));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(FlagTest, TestGetViaReflection) {
+ auto* handle = flags::FindCommandLineFlag("test_flag_01");
+ EXPECT_EQ(*handle->Get<bool>(), true);
+ handle = flags::FindCommandLineFlag("test_flag_02");
+ EXPECT_EQ(*handle->Get<int>(), 1234);
+ handle = flags::FindCommandLineFlag("test_flag_03");
+ EXPECT_EQ(*handle->Get<int16_t>(), -34);
+ handle = flags::FindCommandLineFlag("test_flag_04");
+ EXPECT_EQ(*handle->Get<uint16_t>(), 189);
+ handle = flags::FindCommandLineFlag("test_flag_05");
+ EXPECT_EQ(*handle->Get<int32_t>(), 10765);
+ handle = flags::FindCommandLineFlag("test_flag_06");
+ EXPECT_EQ(*handle->Get<uint32_t>(), 40000);
+ handle = flags::FindCommandLineFlag("test_flag_07");
+ EXPECT_EQ(*handle->Get<int64_t>(), -1234567);
+ handle = flags::FindCommandLineFlag("test_flag_08");
+ EXPECT_EQ(*handle->Get<uint64_t>(), 9876543);
+ handle = flags::FindCommandLineFlag("test_flag_09");
+ EXPECT_NEAR(*handle->Get<double>(), -9.876e-50, 1e-55);
+ handle = flags::FindCommandLineFlag("test_flag_10");
+ EXPECT_NEAR(*handle->Get<float>(), 1.234e12f, 1e5f);
+ handle = flags::FindCommandLineFlag("test_flag_11");
+ EXPECT_EQ(*handle->Get<std::string>(), "");
+ handle = flags::FindCommandLineFlag("test_flag_12");
+ EXPECT_EQ(*handle->Get<absl::Duration>(), absl::Minutes(10));
}
// --------------------------------------------------------------------
@@ -416,28 +474,32 @@ int GetDflt1() { return 1; }
} // namespace
-ABSL_FLAG(int, test_flag_12, GetDflt1(), "test flag 12");
-ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"),
- "test flag 13");
+ABSL_FLAG(int, test_int_flag_with_non_const_default, GetDflt1(),
+ "test int flag non const default");
+ABSL_FLAG(std::string, test_string_flag_with_non_const_default,
+ absl::StrCat("AAA", "BBB"), "test string flag non const default");
namespace {
TEST_F(FlagTest, TestNonConstexprDefault) {
- EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1);
- EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB");
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_int_flag_with_non_const_default), 1);
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_string_flag_with_non_const_default),
+ "AAABBB");
}
// --------------------------------------------------------------------
} // namespace
-ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14"));
+ABSL_FLAG(bool, test_flag_with_non_const_help, true,
+ absl::StrCat("test ", "flag ", "non const help"));
namespace {
#if !ABSL_FLAGS_STRIP_HELP
TEST_F(FlagTest, TestNonConstexprHelp) {
- EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14");
+ EXPECT_EQ(FLAGS_test_flag_with_non_const_help.Help(),
+ "test flag non const help");
}
#endif //! ABSL_FLAGS_STRIP_HELP
@@ -503,14 +565,14 @@ std::string AbslUnparseFlag(const CustomUDT& f) {
} // namespace
-ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15");
+ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT");
namespace {
TEST_F(FlagTest, TestCustomUDT) {
- EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1));
- absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3));
- EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3));
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1));
+ absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3));
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3));
}
// MSVC produces link error on the type mismatch.
@@ -570,16 +632,17 @@ std::string AbslUnparseFlag(const ConversionTestVal& val) {
// Flag default values can be specified with a value that converts to the flag
// value type implicitly.
-ABSL_FLAG(ConversionTestVal, test_flag_16,
- ConversionTestVal::ViaImplicitConv::kTen, "test flag 16");
+ABSL_FLAG(ConversionTestVal, test_flag_implicit_conv,
+ ConversionTestVal::ViaImplicitConv::kTen,
+ "test flag init via implicit conversion");
namespace {
TEST_F(FlagTest, CanSetViaImplicitConversion) {
- EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10);
- absl::SetFlag(&FLAGS_test_flag_16,
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 10);
+ absl::SetFlag(&FLAGS_test_flag_implicit_conv,
ConversionTestVal::ViaImplicitConv::kEleven);
- EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 11);
+ EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 11);
}
// --------------------------------------------------------------------
@@ -646,3 +709,69 @@ TEST_F(FlagTest, TestRetiredFlagRegistration) {
}
} // namespace
+
+// --------------------------------------------------------------------
+
+namespace {
+
+// User-defined type with small alignment, but size exceeding 16.
+struct SmallAlignUDT {
+ SmallAlignUDT() : c('A'), s(12) {}
+ char c;
+ int16_t s;
+ char bytes[14];
+};
+
+bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) {
+ return true;
+}
+std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; }
+
+// User-defined type with small size, but not trivially copyable.
+struct NonTriviallyCopyableUDT {
+ NonTriviallyCopyableUDT() : c('A') {}
+ NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {}
+ NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) {
+ c = rhs.c;
+ return *this;
+ }
+
+ char c;
+};
+
+bool AbslParseFlag(absl::string_view, NonTriviallyCopyableUDT*, std::string*) {
+ return true;
+}
+std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; }
+
+} // namespace
+
+ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help");
+ABSL_FLAG(NonTriviallyCopyableUDT, test_flag_ntc_udt, {}, "help");
+
+namespace {
+
+TEST_F(FlagTest, TestSmallAlignUDT) {
+ SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt);
+ EXPECT_EQ(value.c, 'A');
+ EXPECT_EQ(value.s, 12);
+
+ value.c = 'B';
+ value.s = 45;
+ absl::SetFlag(&FLAGS_test_flag_sa_udt, value);
+ value = absl::GetFlag(FLAGS_test_flag_sa_udt);
+ EXPECT_EQ(value.c, 'B');
+ EXPECT_EQ(value.s, 45);
+}
+
+TEST_F(FlagTest, TestNonTriviallyCopyableUDT) {
+ NonTriviallyCopyableUDT value = absl::GetFlag(FLAGS_test_flag_ntc_udt);
+ EXPECT_EQ(value.c, 'A');
+
+ value.c = 'B';
+ absl::SetFlag(&FLAGS_test_flag_ntc_udt, value);
+ value = absl::GetFlag(FLAGS_test_flag_ntc_udt);
+ EXPECT_EQ(value.c, 'B');
+}
+
+} // namespace
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index f3c424ad..089567f7 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -92,9 +92,9 @@ class FlagState : public flags_internal::FlagStateInterface {
counter_(counter) {}
~FlagState() override {
- if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kHeapAllocated)
+ if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer)
return;
- flags_internal::Delete(flag_impl_->op_, value_.dynamic);
+ flags_internal::Delete(flag_impl_->op_, value_.heap_allocated);
}
private:
@@ -112,11 +112,11 @@ class FlagState : public flags_internal::FlagStateInterface {
// Flag and saved flag data.
FlagImpl* flag_impl_;
union SavedValue {
- explicit SavedValue(void* v) : dynamic(v) {}
+ explicit SavedValue(void* v) : heap_allocated(v) {}
explicit SavedValue(int64_t v) : one_word(v) {}
explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {}
- void* dynamic;
+ void* heap_allocated;
int64_t one_word;
flags_internal::AlignedTwoWords two_words;
} value_;
@@ -128,25 +128,33 @@ class FlagState : public flags_internal::FlagStateInterface {
///////////////////////////////////////////////////////////////////////////////
// Flag implementation, which does not depend on flag value type.
+DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {}
+
+void DynValueDeleter::operator()(void* ptr) const {
+ if (op == nullptr) return;
+
+ Delete(op, ptr);
+}
+
void FlagImpl::Init() {
new (&data_guard_) absl::Mutex;
// At this point the default_value_ always points to gen_func.
- std::unique_ptr<void, DynValueDeleter> init_value(
- (*default_value_.gen_func)(), DynValueDeleter{op_});
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kHeapAllocated:
- HeapAllocatedValue() = init_value.release();
+ case FlagValueStorageKind::kAlignedBuffer:
+ (*default_value_.gen_func)(AlignedBufferValue());
break;
case FlagValueStorageKind::kOneWordAtomic: {
- int64_t atomic_value;
- std::memcpy(&atomic_value, init_value.get(), Sizeof(op_));
- OneWordValue().store(atomic_value, std::memory_order_release);
+ alignas(int64_t) std::array<char, sizeof(int64_t)> buf{};
+ (*default_value_.gen_func)(buf.data());
+ auto value = absl::bit_cast<int64_t>(buf);
+ OneWordValue().store(value, std::memory_order_release);
break;
}
case FlagValueStorageKind::kTwoWordsAtomic: {
- AlignedTwoWords atomic_value{0, 0};
- std::memcpy(&atomic_value, init_value.get(), Sizeof(op_));
+ alignas(AlignedTwoWords) std::array<char, sizeof(AlignedTwoWords)> buf{};
+ (*default_value_.gen_func)(buf.data());
+ auto atomic_value = absl::bit_cast<AlignedTwoWords>(buf);
TwoWordsValue().store(atomic_value, std::memory_order_release);
break;
}
@@ -191,15 +199,16 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
res = flags_internal::Clone(op_, default_value_.dynamic_value);
} else {
- res = (*default_value_.gen_func)();
+ res = flags_internal::Alloc(op_);
+ (*default_value_.gen_func)(res);
}
return {res, DynValueDeleter{op_}};
}
void FlagImpl::StoreValue(const void* src) {
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kHeapAllocated:
- Copy(op_, src, HeapAllocatedValue());
+ case FlagValueStorageKind::kAlignedBuffer:
+ Copy(op_, src, AlignedBufferValue());
break;
case FlagValueStorageKind::kOneWordAtomic: {
int64_t one_word_val = 0;
@@ -257,9 +266,9 @@ std::string FlagImpl::DefaultValue() const {
std::string FlagImpl::CurrentValue() const {
auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kHeapAllocated: {
+ case FlagValueStorageKind::kAlignedBuffer: {
absl::MutexLock l(guard);
- return flags_internal::Unparse(op_, HeapAllocatedValue());
+ return flags_internal::Unparse(op_, AlignedBufferValue());
}
case FlagValueStorageKind::kOneWordAtomic: {
const auto one_word_val =
@@ -318,9 +327,9 @@ std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
bool modified = modified_;
bool on_command_line = on_command_line_;
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kHeapAllocated: {
+ case FlagValueStorageKind::kAlignedBuffer: {
return absl::make_unique<FlagState>(
- this, flags_internal::Clone(op_, HeapAllocatedValue()), modified,
+ this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
on_command_line, counter_);
}
case FlagValueStorageKind::kOneWordAtomic: {
@@ -345,8 +354,8 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) {
}
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kHeapAllocated:
- StoreValue(flag_state.value_.dynamic);
+ case FlagValueStorageKind::kAlignedBuffer:
+ StoreValue(flag_state.value_.heap_allocated);
break;
case FlagValueStorageKind::kOneWordAtomic:
StoreValue(&flag_state.value_.one_word);
@@ -363,25 +372,27 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) {
}
template <typename StorageT>
-typename StorageT::value_type& FlagImpl::OffsetValue() const {
+StorageT* FlagImpl::OffsetValue() const {
char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this));
// The offset is deduced via Flag value type specific op_.
size_t offset = flags_internal::ValueOffset(op_);
- return reinterpret_cast<StorageT*>(p + offset)->value;
+ return reinterpret_cast<StorageT*>(p + offset);
}
-void*& FlagImpl::HeapAllocatedValue() const {
- assert(ValueStorageKind() == FlagValueStorageKind::kHeapAllocated);
- return OffsetValue<FlagHeapAllocatedValue>();
+void* FlagImpl::AlignedBufferValue() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kAlignedBuffer);
+ return OffsetValue<void>();
}
+
std::atomic<int64_t>& FlagImpl::OneWordValue() const {
assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
- return OffsetValue<FlagOneWordValue>();
+ return OffsetValue<FlagOneWordValue>()->value;
}
+
std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const {
assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic);
- return OffsetValue<FlagTwoWordsValue>();
+ return OffsetValue<FlagTwoWordsValue>()->value;
}
// Attempts to parse supplied `value` string using parsing routine in the `flag`
@@ -406,9 +417,9 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
void FlagImpl::Read(void* dst) const {
auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) {
- case FlagValueStorageKind::kHeapAllocated: {
+ case FlagValueStorageKind::kAlignedBuffer: {
absl::MutexLock l(guard);
- flags_internal::CopyConstruct(op_, HeapAllocatedValue(), dst);
+ flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst);
break;
}
case FlagValueStorageKind::kOneWordAtomic: {
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index ae42dedc..2ae3dce3 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -46,8 +46,8 @@ namespace flags_internal {
// by function specific to that type with a signature matching FlagOpFn.
enum class FlagOp {
+ kAlloc,
kDelete,
- kClone,
kCopy,
kCopyConstruct,
kSizeof,
@@ -63,13 +63,13 @@ using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*);
template <typename T>
void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3);
-// Deletes memory interpreting obj as flag value type pointer.
-inline void Delete(FlagOpFn op, const void* obj) {
- op(FlagOp::kDelete, obj, nullptr, nullptr);
+// Allocate aligned memory for a flag value.
+inline void* Alloc(FlagOpFn op) {
+ return op(FlagOp::kAlloc, nullptr, nullptr, nullptr);
}
-// Makes a copy of flag value pointed by obj.
-inline void* Clone(FlagOpFn op, const void* obj) {
- return op(FlagOp::kClone, obj, nullptr, nullptr);
+// Deletes memory interpreting obj as flag value type pointer.
+inline void Delete(FlagOpFn op, void* obj) {
+ op(FlagOp::kDelete, nullptr, obj, nullptr);
}
// Copies src to dst interpreting as flag value type pointers.
inline void Copy(FlagOpFn op, const void* src, void* dst) {
@@ -80,6 +80,12 @@ inline void Copy(FlagOpFn op, const void* src, void* dst) {
inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
op(FlagOp::kCopyConstruct, src, dst, nullptr);
}
+// Makes a copy of flag value pointed by obj.
+inline void* Clone(FlagOpFn op, const void* obj) {
+ void* res = flags_internal::Alloc(op);
+ flags_internal::CopyConstruct(op, obj, res);
+ return res;
+}
// Returns true if parsing of input text is successfull.
inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
std::string* error) {
@@ -195,7 +201,7 @@ constexpr FlagHelpArg HelpArg(char) {
// Signature for the function generating the initial flag value (usually
// based on default value supplied in flag's definition)
-using FlagDfltGenFunc = void* (*)();
+using FlagDfltGenFunc = void (*)(void*);
union FlagDefaultSrc {
constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg)
@@ -253,46 +259,36 @@ using FlagUseTwoWordsStorage =
#endif
template <typename T>
-using FlagUseHeapStorage =
+using FlagUseBufferStorage =
std::integral_constant<bool, !FlagUseOneWordStorage<T>::value &&
!FlagUseTwoWordsStorage<T>::value>;
enum class FlagValueStorageKind : uint8_t {
- kHeapAllocated = 0,
+ kAlignedBuffer = 0,
kOneWordAtomic = 1,
kTwoWordsAtomic = 2
};
template <typename T>
static constexpr FlagValueStorageKind StorageKind() {
- return FlagUseHeapStorage<T>::value
- ? FlagValueStorageKind::kHeapAllocated
+ return FlagUseBufferStorage<T>::value
+ ? FlagValueStorageKind::kAlignedBuffer
: FlagUseOneWordStorage<T>::value
? FlagValueStorageKind::kOneWordAtomic
- : FlagUseTwoWordsStorage<T>::value
- ? FlagValueStorageKind::kTwoWordsAtomic
- : FlagValueStorageKind::kHeapAllocated;
+ : FlagValueStorageKind::kTwoWordsAtomic;
}
-struct FlagHeapAllocatedValue {
- using value_type = void*;
-
- value_type value;
-};
-
struct FlagOneWordValue {
- using value_type = std::atomic<int64_t>;
constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {}
- value_type value;
+ std::atomic<int64_t> value;
};
struct FlagTwoWordsValue {
- using value_type = std::atomic<AlignedTwoWords>;
constexpr FlagTwoWordsValue()
: value(AlignedTwoWords{UninitializedFlagValue(), 0}) {}
- value_type value;
+ std::atomic<AlignedTwoWords> value;
};
template <typename T,
@@ -300,9 +296,10 @@ template <typename T,
struct FlagValue;
template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kHeapAllocated>
- : FlagHeapAllocatedValue {
+struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
bool Get(T*) const { return false; }
+
+ alignas(T) char value[sizeof(T)];
};
template <typename T>
@@ -347,10 +344,8 @@ struct FlagCallback {
// The class encapsulates the Flag's data and access to it.
struct DynValueDeleter {
- explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {}
- void operator()(void* ptr) const {
- if (op != nullptr) flags_internal::Delete(op, ptr);
- }
+ explicit DynValueDeleter(FlagOpFn op_arg = nullptr);
+ void operator()(void* ptr) const;
FlagOpFn op;
};
@@ -416,10 +411,10 @@ class FlagImpl final : public flags_internal::CommandLineFlag {
// it is only used inside the three routines below, which are defined in
// flag.cc, we can define it in that file as well.
template <typename StorageT>
- typename StorageT::value_type& OffsetValue() const;
- // This is an accessor for a value stored in heap allocated storage.
- // Returns a mutable reference to a pointer to allow vlaue mutation.
- void*& HeapAllocatedValue() const;
+ StorageT* OffsetValue() const;
+ // This is an accessor for a value stored in an aligned buffer storage.
+ // Returns a mutable pointer to the start of a buffer.
+ void* AlignedBufferValue() const;
// This is an accessor for a value stored as one word atomic. Returns a
// mutable reference to an atomic value.
std::atomic<int64_t>& OneWordValue() const;
@@ -492,17 +487,8 @@ class FlagImpl final : public flags_internal::CommandLineFlag {
// Kind of storage this flag is using for the flag's value.
const uint8_t value_storage_kind_ : 2;
- // ------------------------------------------------------------------------
- // The bytes containing the const bitfields must not be shared with bytes
- // containing the mutable bitfields.
- // ------------------------------------------------------------------------
-
- // Unique tag for absl::call_once call to initialize this flag.
- //
- // The placement of this variable between the immutable and mutable bitfields
- // is important as prevents them from occupying the same byte. If you remove
- // this variable, make sure to maintain this property.
- absl::once_flag init_control_;
+ uint8_t : 0; // The bytes containing the const bitfields must not be
+ // shared with bytes containing the mutable bitfields.
// Mutable flag's state (guarded by `data_guard_`).
@@ -514,6 +500,9 @@ class FlagImpl final : public flags_internal::CommandLineFlag {
// Has this flag been specified on command line.
bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Unique tag for absl::call_once call to initialize this flag.
+ absl::once_flag init_control_;
+
// Mutation counter
int64_t counter_ ABSL_GUARDED_BY(*DataGuard());
// Optional flag's callback and absl::Mutex to guard the invocations.
@@ -600,11 +589,17 @@ class Flag {
template <typename T>
void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
switch (op) {
- case FlagOp::kDelete:
- delete static_cast<const T*>(v1);
+ case FlagOp::kAlloc: {
+ std::allocator<T> alloc;
+ return std::allocator_traits<std::allocator<T>>::allocate(alloc, 1);
+ }
+ case FlagOp::kDelete: {
+ T* p = static_cast<T*>(v2);
+ p->~T();
+ std::allocator<T> alloc;
+ std::allocator_traits<std::allocator<T>>::deallocate(alloc, p, 1);
return nullptr;
- case FlagOp::kClone:
- return new T(*static_cast<const T*>(v1));
+ }
case FlagOp::kCopy:
*static_cast<T*>(v2) = *static_cast<const T*>(v1);
return nullptr;
@@ -675,13 +670,13 @@ class FlagRegistrar {
struct EmptyBraces {};
template <typename T>
-T* MakeFromDefaultValue(T t) {
- return new T(std::move(t));
+void MakeFromDefaultValue(void* dst, T t) {
+ new (dst) T(std::move(t));
}
template <typename T>
-T* MakeFromDefaultValue(EmptyBraces) {
- return new T{};
+void MakeFromDefaultValue(void* dst, EmptyBraces) {
+ new (dst) T{};
}
} // namespace flags_internal