int to const char* at compile time

25 views
Skip to first unread message

Łukasz Anforowicz

unread,
Jun 30, 2017, 11:07:17 AM6/30/17
to cxx
Hello,

I want to make my feature configurable, so that it can operate in a few distinct modes.  I plan to 1) represent the different modes as enum values (rathar than as strings) and 2) make the modes selectable via Finch, chrome://flags and command line.  The latter can be accomplished by hooking into Finch params - this works and I can relatively easily use GetFieldTrialParamByFeatureAsInt to convert the string value to an integer/enum and to fallback to a default value.  OTOH, integration with chrome://flags looks a bit clunky, because it requires specifying param values as const char* (flags_ui::FeatureEntry::FeatureParam::param_value) - what are my options here?

Option 1: Hardcode "1", "2", etc.:
const FeatureEntry::FeatureParam kTopDocumentIsolationVariations_Xsite[] = {
    {features::kTopDocumentIsolationModeParam, "2"}}; 

Option 2: Poor man's int -> const char* conversion at compile time (all my enum values are less than 9):
const char kTopDocumentIsolationVariations_Xsite_Value[] = {
    '0' + static_cast<int>(features::TopDocumentIsolationMode::Xsite), '\0'};  // this is my "string" / const char*...
const FeatureEntry::FeatureParam kTopDocumentIsolationVariations_Xsite[] = {
    {features::kTopDocumentIsolationModeParam,
     kTopDocumentIsolationVariations_Xsite_Value}};

Option 3: Fancy (and a bit ugly) template-based conversion

Q1: Are there other options that I miss?  FWIW, 1) std::to_string is not constexpr and besides 2) I think I really need a const char*, rather than a std::string here. 

Q2: Which option would you choose (I am currently leaning toward option #2)?

Q3: Is option #3 something that we might consider adopting in Chromium (say, in base/numerics/compile_time_conversions.h)?


 

Thanks,

Lukasz

Łukasz Anforowicz

unread,
Jun 30, 2017, 11:07:52 AM6/30/17
to cxx, Alexei Svitkine
<+asvitkine@, since this is somewhat related to base::Feature>

Nico Weber

unread,
Jun 30, 2017, 11:13:58 AM6/30/17
to Łukasz Anforowicz, cxx, Alexei Svitkine
The old timey way of doing this is to have a MyStuff.def file looking like so:

FEAT(FirstFeat)
FEAT(SecondFeat)

And then do

enum kFeatEnums {
#define FEAT(x) k ## x,
#include "MyStuff.def"
#undef FEAT
};

#define FEAT(x) const char k ## x ## Name = #x;
#include "MyStuff.def"
#undef

...and then you have kFirstFeat as enum with value 0 and kFirstFeatName with value "FeatName". That seems less clever than your options.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/6cef7d4e-64db-4453-b040-f77765c8327a%40chromium.org.

Łukasz Anforowicz

unread,
Jun 30, 2017, 11:39:00 AM6/30/17
to cxx, luk...@chromium.org, asvi...@chromium.org
Thanks Nico for pointing out the macro-based approach (there is also an X macros technique which avoids a separate .def file).  AFAIU given enum MyEnum { FooBar = 123 }, the macros you gave would get provide a compile time string "FooBar", rather than "123" (as I originally asked for).  I think that "123" is easier to work (e.g. GetFieldTrialParamByFeatureAsInt can be used to easily ignore invalid inputs), but maybe I should indeed consider "FooBar" (because no matter what, I will still have to hardcode these strings in a Finch config).

-Lukasz

Scott Graham

unread,
Jun 30, 2017, 11:51:18 AM6/30/17
to Łukasz Anforowicz, cxx, Alexei Svitkine
Normally, you'd just put both the enum name and its value into the X(a,b) and then use it multiple times to define both the enum and any secondary tables, like the stringized ints.

Łukasz Anforowicz

unread,
Jun 30, 2017, 12:45:22 PM6/30/17
to cxx, luk...@chromium.org, asvi...@chromium.org
Doh - you're right, I can just pass both the name and the value as arguments of the macro.  I really like this approach - in the future, I can add new modes to the FOR_EACH_TDI_MODE macro and nothing in about_flags.cc will need to change.  Thanks!

https://codereview.chromium.org/2946113002/patch/100001/110017 - somewhere in a public //content header (currently in content_features.h...):
CONTENT_EXPORT extern const char kTopDocumentIsolationModeParam[];
#define FOR_EACH_TDI_MODE(V)                                                  \
  V(Xsite, 1, "isolate all frames from sites other than the top-level frame") \
  V(Ads, 2, "isolate ads detected by heuristics")

enum class TopDocumentIsolationMode {
  Default = 0,

#define DEFINE_TDI_MODE_ENUM_VALUE(name, value, description) name = value,
  FOR_EACH_TDI_MODE(DEFINE_TDI_MODE_ENUM_VALUE)
#undef DEFINE_TDI_MODE_ENUM_VALUE
};

#define DEFINE_TDI_MODE_FEATURE_PARAM(name, value, description)               \
  const FeatureEntry::FeatureParam kTopDocumentIsolationVariations_##name[] = \
      {{features::kTopDocumentIsolationModeParam, #value}};
FOR_EACH_TDI_MODE(DEFINE_TDI_MODE_FEATURE_PARAM)
#undef DEFINE_TDI_MODE_FEATURE_PARAM

const FeatureEntry::FeatureVariation kTopDocumentIsolationVariations[] = {
#define DEFINE_TDI_MODE_VARIATION(name, value, description)                 \
  {"(" #name " - " description ")", kTopDocumentIsolationVariations_##name, \
   arraysize(kTopDocumentIsolationVariations_##name), nullptr},
    FOR_EACH_TDI_MODE(DEFINE_TDI_MODE_VARIATION)
#undef DEFINE_TDI_MODE_VARIATION
};

Reply all
Reply to author
Forward
0 new messages