Commit 4b5d7fec authored by Anton Likhtarov's avatar Anton Likhtarov Committed by Facebook Github Bot

Initial support for user-defined types

Summary:
The type must provide a StringPiece constructor or be convertible
via `folly::to<UserDefinedType>(string)`; toAppend() must also be defined for
the reverse construction.

Differential Revision: D8586230

fbshipit-source-id: 80159bc1cc9d32f0ae40af8f053387f341bea835
parent c2040e01
......@@ -85,8 +85,11 @@ class Setting {
* the order of minutes).
*
* @param reason Will be stored with the current value, useful for debugging.
* @throws std::runtime_error If we can't convert t to string.
*/
void set(const Type& t, StringPiece reason = "api") {
/* Check that we can still display it */
folly::to<std::string>(t);
core_.set(t, reason);
}
......@@ -98,6 +101,14 @@ class Setting {
SettingCore<Type> core_;
};
/* C++20 has std::type_indentity */
template <class T>
struct TypeIdentity {
using type = T;
};
template <class T>
using TypeIdentityT = typename TypeIdentity<T>::type;
} // namespace detail
/**
......@@ -128,7 +139,7 @@ class Setting {
setting( \
folly::settings::SettingMetadata{ \
#_project, #_name, #_Type, typeid(_Type), #_def, _desc}, \
_def); \
folly::settings::detail::TypeIdentityT<_Type>{_def}); \
return *setting; \
} \
/* Ensure the setting is registered even if not used in program */ \
......
......@@ -54,13 +54,24 @@ class SettingCoreBase {
void registerSetting(SettingCoreBase& core);
template <class T>
std::enable_if_t<std::is_constructible<T, StringPiece>::value, T>
convertOrConstruct(StringPiece newValue) {
return T(newValue);
}
template <class T>
std::enable_if_t<!std::is_constructible<T, StringPiece>::value, T>
convertOrConstruct(StringPiece newValue) {
return to<T>(newValue);
}
template <class Type>
class SettingCore : public SettingCoreBase {
public:
using Contents = SettingContents<Type>;
void setFromString(StringPiece newValue, StringPiece reason) override {
set(to<Type>(newValue), reason.str());
set(convertOrConstruct<Type>(newValue), reason.str());
}
std::pair<std::string, std::string> getAsString() const override {
auto contents = *const_cast<SettingCore*>(this)->tlValue();
......
......@@ -33,14 +33,99 @@ FOLLY_SETTING_DEFINE(
std::string,
"unused_default",
"Not used, but should still be in the list");
FOLLY_SETTING_DEFINE(
follytest,
multi_token_type,
unsigned int,
123,
"Test that multi-token type names can be used");
// Enable to test runtime collision checking logic
#if 0
FOLLY_SETTING_DEFINE(follytest, internal_flag_to_a, std::string,
"collision_with_a",
"Collision_with_a");
#endif
/* Test user defined type support */
struct UserDefinedType {
explicit UserDefinedType(folly::StringPiece value) {
if (value == "a") {
value_ = 0;
} else if (value == "b") {
value_ = 100;
} else {
throw std::runtime_error("Invalid value passed to UserDefinedType ctor");
}
}
int value_;
};
/* Note: conversion intentionally to different strings to test that this
function is called */
template <class String>
void toAppend(const UserDefinedType& t, String* out) {
if (t.value_ == 0) {
out->append("a_out");
} else if (t.value_ == 100) {
out->append("b_out");
} else {
throw std::runtime_error("Can't convert UserDefinedType to string");
}
}
FOLLY_SETTING_DEFINE(
follytest,
user_defined,
UserDefinedType,
"b",
"User defined type constructed from string");
} // namespace some_ns
TEST(Settings, user_defined) {
EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 100);
EXPECT_TRUE(
folly::settings::setFromString("follytest_user_defined", "a", "test"));
EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 0);
{
auto info = folly::settings::getAsString("follytest_user_defined");
EXPECT_TRUE(info.hasValue());
EXPECT_EQ(info->first, "a_out");
EXPECT_EQ(info->second, "test");
}
EXPECT_THROW(
folly::settings::setFromString("follytest_user_defined", "c", "test2"),
std::runtime_error);
EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 0);
{
auto info = folly::settings::getAsString("follytest_user_defined");
EXPECT_TRUE(info.hasValue());
EXPECT_EQ(info->first, "a_out");
EXPECT_EQ(info->second, "test");
}
EXPECT_TRUE(folly::settings::resetToDefault("follytest_user_defined"));
EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 100);
{
auto info = folly::settings::getAsString("follytest_user_defined");
EXPECT_TRUE(info.hasValue());
EXPECT_EQ(info->first, "b_out");
EXPECT_EQ(info->second, "default");
}
/* Test that intentionally setting to something non-converteable fails */
some_ns::UserDefinedType bad("a");
bad.value_ = 50;
EXPECT_THROW(
some_ns::FOLLY_SETTING(follytest, user_defined).set(bad),
std::runtime_error);
EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 100);
{
auto info = folly::settings::getAsString("follytest_user_defined");
EXPECT_TRUE(info.hasValue());
EXPECT_EQ(info->first, "b_out");
EXPECT_EQ(info->second, "default");
}
}
TEST(Settings, basic) {
EXPECT_EQ(a_ns::a_func(), 1245);
EXPECT_EQ(b_ns::b_func(), "testbasdf");
......@@ -104,6 +189,10 @@ TEST(Settings, basic) {
EXPECT_EQ(meta.typeStr, "int");
} else if (meta.typeId == typeid(std::string)) {
EXPECT_EQ(meta.typeStr, "std::string");
} else if (meta.typeId == typeid(unsigned int)) {
EXPECT_EQ(meta.typeStr, "unsigned int");
} else if (meta.typeId == typeid(some_ns::UserDefinedType)) {
EXPECT_EQ(meta.typeStr, "UserDefinedType");
} else {
ASSERT_FALSE(true);
}
......@@ -121,10 +210,12 @@ TEST(Settings, basic) {
allFlags,
"follytest/internal_flag_to_a/int/789/Desc of int/789/default\n"
"follytest/internal_flag_to_b/std::string/\"test\"/Desc of str/test/default\n"
"follytest/multi_token_type/unsigned int/123/Test that multi-token type names can be used/123/default\n"
"follytest/public_flag_to_a/int/456/Public flag to a/300/from_string\n"
"follytest/public_flag_to_b/std::string/\"basdf\"/Public flag to b/basdf/default\n"
"follytest/some_flag/std::string/\"default\"/Description/default/default\n"
"follytest/unused/std::string/\"unused_default\"/Not used, but should still be in the list/unused_default/default\n");
"follytest/unused/std::string/\"unused_default\"/Not used, but should still be in the list/unused_default/default\n"
"follytest/user_defined/UserDefinedType/\"b\"/User defined type constructed from string/b_out/default\n");
EXPECT_TRUE(folly::settings::resetToDefault("follytest_public_flag_to_a"));
EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 456);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment