how to use traits to access compile-time const value? - c++

I am working on a project where the behavior of a certain function need to switch between a few values:
class James{
public:
James(){
if(a==0){
//do this
}else{
// do that
}
}
};
currently, 'a' is read from a config file at runtime. However, in practice, 'a' can be determined at compile-time, rather than run time. I am thinking about have a trait class
struct TraitZero{
constexpr int a = 0;
};
struct TraitOne{
constexpr int a = 1;
};
and then turn James into a template class
template<typename Trait>
class James{
constexpr int a = Trait::a;
public:
James(){
if(a=0){
//do this
}else{
// do that
}
}
};
I don't know where I got it wrong, but this does not compile.
I am wondering if anyone here has ever countered problems like this. Can anyone share some insights?

As has already been mentioned by skypjack, only static data members can be constexpr, and you need to use == instead of = in the conditional.
That said, since you want to determine a at compile time, it may be beneficial to you to branch based on a at compile time, as well. To do this, you can use SFINAE or (as of C++17) constexpr if.
Assuming the following three traits...
struct TraitZero{
static constexpr int a = 0;
};
struct TraitOne{
static constexpr int a = 1;
};
template<size_t N>
struct TraitN {
static constexpr int a = N;
};
We can do this as...
SFINAE:
template<typename Trait>
class James {
// Unnecessary, we can access Trait::a directly.
//static constexpr int a = Trait::a;
public:
template<bool AZero = Trait::a == 0>
James(std::enable_if_t<AZero, unsigned> = 0) {
std::cout << "Trait::a is 0.\n";
}
template<bool AOne = Trait::a == 1>
James(std::enable_if_t<AOne, int> = 0) {
std::cout << "Trait::a is 1.\n";
}
template<bool ANeither = (Trait::a != 0) && (Trait::a != 1)>
James(std::enable_if_t<ANeither, long> = 0) {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
};
What this does is conditionally select one of the versions of James() based on the value of Traits::a, using dummy parameters to enable overloading; this is simpler for functions other than constructors and destructors, as enable_if can be used on their return type.
Note the use of template parameters, instead of directly checking Trait::a in the enable_ifs themselves. As SFINAE can only be performed with types and expressions in the immediate context of the functions, these are used to "drag it in", so to speak; I like to perform the logic while doing so, as it minimises the intrusiveness of the enable_if.
constexpr if:
template<typename Trait>
class James {
// Unnecessary, we can access Trait::a directly.
//static constexpr int a = Trait::a;
public:
James() {
if constexpr (Trait::a == 0) {
std::cout << "Trait::a is 0.\n";
} else if constexpr (Trait::a == 1) {
std::cout << "Trait::a is 1.\n";
} else {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
}
};
As can be seen here, constexpr if can be used to create cleaner, more natural code than SFINAE, with the advantage that it will still be evaluated at compile time instead of run time; unfortunately, it isn't yet supported by most compilers. [In this particular case, each version of James() will also be one machine instruction shorter (when compiled with GCC 7.0), due to not using a dummy parameter to differentiate between overloads.]
More specifically, with constexpr if, statement-false is discarded if the condition is true, and statement-true is discarded if it's false; in effect, this basically means that the compiler sees the entire constexpr if statement as the branch which would be executed. In this case, for example, the compiler will generate one of the following three functions, based on the value of Trait::a.
// If Trait::a == 0:
James() {
std::cout << "Trait::a is 0.\n";
}
// If Trait::a == 1:
James() {
std::cout << "Trait::a is 1.\n";
}
// If Trait::a == anything else:
James() {
std::cout << "Trait::a is neither 0 nor 1.\n";
}
In either case, with the following code...
int main() {
James<TraitZero> j0;
James<TraitOne> j1;
James<TraitN<2>> j2;
}
The following output is generated:
Trait::a is 0.
Trait::a is 1.
Trait::a is neither 0 nor 1.
Each type's constructor will be coded specifically to output the appropriate line, and none of the three constructors will actually contain any branching.
Note that I only marked member a as unnecessary out of personal preference; since I can access Trait::a directly, I prefer to do so, so I won't have to check what a is if I ever forgoet. Feel free to use it if you want to, or if it's needed elsewhere.

a data members must be declared as constexpr and static to be used the way you are trying to use them:
struct TraitZero{
static constexpr int a = 0;
};
struct TraitOne{
static constexpr int a = 1;
};
Put aside the fact that it's be ill-formed as it stands, you wouldn't be allowed to access it as Traits::a otherwise.
The same applies to the class James:
template<typename Trait>
class James{
static constexpr int a = Trait::a;
//...
};
Note also that probably the following isn't what you want:
if(a=0){
Even if you were allowed to modify a (and you are not for it's a static constexpr data member), in this case you would have assigned 0 to a and constantly got the else branch.
Most likely you were looking for something similar but slightly different:
if(a == 0){
Below is an example based on your code once fixed:
#include<iostream>
struct TraitZero{
static constexpr int a = 0;
};
struct TraitOne{
static constexpr int a = 1;
};
template<typename Trait>
class James{
static constexpr int a = Trait::a;
public:
James(){
if(a==0){
std::cout << "0" << std::endl;
}else{
std::cout << "1" << std::endl;
}
}
};
int main() {
James<TraitZero> j0;
James<TraitOne> j1;
}

Why not pass a #define at compile time using the -D option?
For example: Including a #define in all .c source files at compile time

Related

Tuple of all complete types satisfying a certain concept

I want to get all complete types that satisfy a certain concept. The types should be listed as element types of a std::tuple.
For:
#include <concepts>
#include <tuple>
class A{};
template<typename T>
concept InterestingType = std::derived_from<T, A>;
class B : public A{};
class C : public A{};
class D{};
I would want to get the type std::tuple<A, B, C> (because A, B and C satisfy the concept InterestingType but D doesn't.). The order of the classes in the tuple doesn't matter (e.g. std::tuple<C, B, A> is also fine). Duplicates would probably also be fine (e.g. std::tuple<A, B, C, A, A>).
Is this possible?
update: As asked in the comments, here the planned purpose of an answer to this question:
I have a number of predefined classes which have a string identifier and provide a parsing constructor which interprets a string, e.g.:
struct MyInt {
static constexpr char name[] = "MyInt";
int i_;
MyInt(std::string);
}
struct MyDouble {
static constexpr char name[] = "MyDouble";
double d_;
MyInt(std::string);
}
And a factory method:
std::any make(std::string type_name, std::string constructor_arg){
// generated code
if (type_name == MyInt::name)
return MyInt{constructor_arg};
else if (type_name == MyDouble::name)
return MyDouble{constructor_arg};
else
return {};
}
make calls actually a compile-time generated switch-case-like template function which takes a tuple of types (i.e. std::tuple<MyInt, MyDouble>) as template argument and generates code equivalent to the implementation provided above.
I currently define the tuple of registered types manually.
This is OK for predefined types shipped with the library.
What I am struggling with (and why I asked the first question), is how a user can provide additional types and register them.
final note: A virtual constructor is for other technical reasons no option. The registered types (like MyInt and MyDouble) cannot have virtual methods for other technical reasons.
update 2:
I've build an working example with the code below from HolyBlackCat's answer below. It compiles fine with clang >= 10 but fails on all gcc versions. Checkout https://godbolt.org/z/9xxbo79rr
Any idea what I need to change so that it builds with gcc? At least gcc 11 should be able to compile it.
#include <iostream>
#include <any>
#include <string>
#include <map>
using func_ptr = std::any (*)(std::string);
using func_map = std::map<std::string, func_ptr>;
struct Factory {
inline static func_map &make_funcs() {
static func_map func_ptrs;
return func_ptrs;
}
};
template<typename T>
struct Base {
inline static const std::nullptr_t dummy = [] {
// This code will run for each derived class, when the program starts.
Factory::make_funcs()[T::name] = [](std::string s) -> std::any { return T{std::move(s)}; };
return nullptr;
}();
// Force `dummy` to be instantiated, even though it's unused.
static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};
struct MyInt : Base<MyInt> {
static constexpr char name[] = "MyInt";
int i_ = 5;
MyInt(std::string) {}
};
struct MyDouble : Base<MyDouble> {
static constexpr char name[] = "MyDouble";
double d_ = 4.0;
MyDouble(std::string) {}
};
int main() {
for (auto &[_, make] : Factory::make_funcs()) {
auto instance = make("a");
try {
any_cast<MyDouble>(instance);
std::cout << "is MyDouble" << std::endl;
} catch (...) {}
try {
any_cast<MyInt>(instance);
std::cout << "is MyInt" << std::endl;
} catch (...) {}
}
}
update 3:
It works by making the init function of dummy a separate function which is not defined in the class. See https://godbolt.org/z/hnW58zc4s
#include <iostream>
#include <any>
#include <string>
#include <map>
using func_ptr = std::any (*)(std::string);
using func_map = std::map<std::string, func_ptr>;
struct Factory {
inline static func_map &make_funcs() {
static func_map func_ptrs;
return func_ptrs;
}
};
template<typename T>
struct Base {
inline static std::nullptr_t init();
inline static const std::nullptr_t dummy = init();
// Force `dummy` to be instantiated, even though it's unused.
static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};
template<typename T>
std::nullptr_t Base<T>::init() {
Factory::make_funcs()[T::name] = [](std::string s) -> std::any { return T{std::move(s)}; };
return nullptr;
}
struct MyInt : Base<MyInt> {
static constexpr char name[] = "MyInt";
int i_ = 5;
MyInt(std::string) {}
};
struct MyDouble : Base<MyDouble> {
static constexpr char name[] = "MyDouble";
double d_ = 4.0;
MyDouble(std::string) {}
};
int main() {
for (auto &[_, make] : Factory::make_funcs()) {
auto instance = make("a");
try {
any_cast<MyDouble>(instance);
std::cout << "is MyDouble" << std::endl;
} catch (...) {}
try {
any_cast<MyInt>(instance);
std::cout << "is MyInt" << std::endl;
} catch (...) {}
}
}
Getting "all complete types that satisfy a certain concept" is impossible (at least before we get reflection), so I'm answering the second part of the question.
Step 1: You might want to use something like this to get type names, this way you don't need the name variable in classes.
Step 2: Make a CRTP base, and inherit your classes from it:
template <typename T> struct Base {};
struct MyInt : Base<MyInt> {};
struct MyDouble : Base<MyDouble> {};
Step 3: Inject class registration code into the base:
template <typename T>
class Base
{
static std::nullptr_t Register()
{
// This function will run for each derived class, when the program starts.
std::ios_base::Init init; // Without this, `std::cout` might not work this early.
std::cout << "Hello, world!\n";
return nullptr;
}
inline static const std::nullptr_t dummy = Register();
// Force `dummy` to be instantiated, even though it's unused.
static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};
This code will run when your program starts, before entering main, for each class derived from Base<...>.
Step 4: Create a singleton, something like std::map<std::string, std::any(*)()>, to store functions to construct each class. Populate it from a static variable initializer in class Base, as shown above.
Make sure the singleton is a static variable in a function, not a global variable. Otherwise you'll experience the static init order fiasco.

Avoid "recursive" function calls while unpacking variadic templates until runtime condition

Basically, I want to find a template in a parameter pack that satisfies some runtime conditions. Intuitively, I just want to iterate over my instantiations of the parameter pack and find the first which satisfies a condition. My current simplified toy implementation to demonstrate what I mean:
Find the struct of X and Y which satisfies their test() first.
struct X {
bool test(int i) {
flag = i > 10;
return flag;
}
bool flag;
std::string value = "X satisfied first";
};
struct Y {
bool test(int i) {
flag = i > 11;
return flag;
}
bool flag;
std::string value = "Y satiesfied first";
};
This struct finds the first struct of X and Y to satisfy the condition. In this example it increases an integer up to a given limit until one of the structs reports that its test() was successful.
template <typename... Ts> struct FindFirst {
static std::string find_first(int limit) {
return find_first_satisfying(limit, Ts{}...);
}
static std::string find_first_satisfying(int limit, Ts... ts) {
int i = 0;
bool satisfied = false;
while (i < limit && !satisfied) {
satisfied = (ts.test(i) || ...);
i++;
}
return extract(ts...);
}
template <typename T, typename... OtherTs>
static std::string extract(T t, OtherTs... ts) {
if (t.flag) {
return t.value;
} else {
if constexpr (sizeof...(OtherTs) > 0) {
return extract(ts...);
} else {
return "Nobody satiesfied condition";
}
}
}
};
This implementation generates as many different extract() functions with different signatures as there are templates in the pack. They get "recursively" called and result in a deep call stack (depends on the position of the satisfying struct) and large bytecode.
Is there a method to construct a loop (at compile-time) which tests each instantiation of the parameter pack and stops appropriately?
Also, any other suggestions on how to simplify the whole construct?
I would wrote your code something like that:
template <typename ... Ts>
std::string find_first_satisfying(int limit, Ts... ts)
{
for (int i = 0; i != limit; ++i) {
std::string res;
bool found = false;
([&](){ if (ts.test(i)) { found = true; res = ts.value; } return found;}() || ...);
if (found) { return res; }
}
return "Nobody satisfied condition";
}
Demo
No. It is possible that in C++23 it won't be like this but currently there is no guarantee.
But is there problem really? The only issue I see is that the code is hard to write and understand. Large bytecode is of little significance and optimizer should be able to inline and optimize everything - only debug performance should suffer as a result (and compile time)... Unless you write the program in a manner that makes optimizer/compiler unable to inline it (by hiding bodies of functions).
P.S. can't you somehow write extract as an operator and use the ... instead of recursion? Though, I think it is a bad idea for various reasons. (I see that #Jarod42 wrote it via lambda in another answer, it looks good to me.)

C++11 indexing template parameter packs at runtime in order to access Nth type

From this SO topic (and this blog post), I know how to access Nth type in a template parameter pack. For instance, one of the answers to the abovementioned SO question suggests this:
template<int N, typename... Ts> using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type;
using ThirdType = NthTypeOf<2, Ts...>;
However, these methods work only in compile-time. Trying to do something such as:
int argumentNumber = 2;
using ItsType = NthTypeOf<argumentNumber, Arguments...>;
would result in compile error:
Error : non-type template argument is not a constant expression
Is there a way to access Nth type at runtime?
Here's my use case:
My program reads a text file, which is basically an array of numbers. Each number i refers to the i-th type of a template parameter pack that my class is templated based on. Based on that type, I want to declare a variable of that type and do something differently with it. For example, if it's a string, I want to declare a string and do string matching, and if it's an integer, I would like to compute the square root of a number.
C++ is a statically​ typed language. As such the type of all variables needs to be known at compile time (and cannot vary). You want a type that depends on a runtime value. Luckily C++ also features dynamic typing of objects.
Warning: all code in this answer serves only for demonstration of the basic concept/idea. It's missing any kind of error handling, sane interfaces (constructors...), exception safety, ... . So don't use for production, consider using the implementations​ available from boost.
To use this feature you need what's called a polymorphic base class: a class with (at least) one virtual member function from which you derive further classes.
struct value_base {
// you want to be able to make copies
virtual std::unique_ptr<value_base> copy_me() const = 0;
virtual ~value_base () {}
};
template<typename Value_Type>
struct value_of : value_base {
Value_Type value;
std::unique_ptr<value_base> copy_me() const {
return new value_of {value};
}
};
You can then have a variable with static type of pointer or reference to that base class, which can point to/reference objects from both the base class as well as from any of those derived classes. If you have a clearly defined interface, then encode that as virtual member functions (think of Shape and area (), name (), ... functions) and make calls through that base class pointer/reference (as shown in the other answer). Otherwise use a (hidden) dynamic cast to obtain a pointer/reference with static type of the dynamic type:
struct any {
std:: unique_ptr<value_base> value_container;
// Add constructor
any(any const & a)
: value_container (a.value_container->copy_me ())
{}
// Move constructor
template<typename T>
T & get() {
value_of<T> * typed_container
= dynamic_cast<value_of<T> *>(value_container.get();)
if (typed_container == nullptr) {
// Stores another type, handle failure
}
return typed_container->value;
}
// T const & get() const;
// with same content as above
};
template<typename T, typename... Args>
any make_any (Args... && args) {
// Raw new, not good, add proper exception handling like make_unique (C++14?)
return {new T(std:: forward<Args>(args)...)};
}
Since object construction is done at runtime the actual type of the pointed to/referenced object may depend on runtime values:
template<typename T>
any read_and_construct (std:: istream & in) {
T value;
// Add error handling please
in >> value;
return make_any<T>(std:: move (value));
}
// ...
// missing: way of error handling
std::map<int, std:: function<any(std:: istream &)>> construction_map;
construction_map.insert(std::make_pair(1, read_and_construct<double>));
// and more
int integer_encoded_type;
// error handling please
cin >> integer_encoded_type;
// error handling please
any value = construction_map [integer_encoded_type] (cin);
As you may have noticed above code uses also a clearly defined interface for construction. If you don't intend to do lots of different things with the returned any objects, potentially storing them in various data structures over great parts of the time your program is running, then using an any type is most likely overkill and you should just put the type dependent code into those construction functions, too.
A serious drawback of such an any class is its generality: it's possible to store just about any type within it. This means that the (maximum) size of the (actually) stored object is not known during compilation, making use of storage with automatic duration (the "stack") impossible (in standard C++). This may lead to expensive usage of dynamic memory (the "heap"), which is considerably slower than automatic memory. This issue will surface whenever many copies of any objects have to be made, but is probably irrelevant (except for cache locality) if you just keep a collection of them around.
Thus, if you know at compile time the set of types which you must be able to store, then you can (at compile time) compute the maximum size needed, use a static array of that size and construct your objects inside that array (since C++11 you can achieve the same with a (recursive template) union, too):
constexpr size_t max_two (size_t a, size_t b) {
return (a > b) ? a : b;
}
template<size_t size, size_t... sizes>
constexpr size_t max_of() {
return max_two (size, max_of<sizes>());
}
template<typename... Types>
struct variant {
alignas(value_of<Types>...) char buffer[max_of<sizeof (value_of<Types>)...>()];
value_base * active;
// Construct an empty variant
variant () : active (nullptr)
{}
// Copy and move constructor still missing!
~variant() {
if (active) {
active->~value_base ();
}
}
template<typename T, typename... Args>
void emplace (Args... && args) {
if (active) {
active->~value_base ();
}
active = new (buffer) T(std:: forward<Args>(args)...);
}
};
C++ is a statically-typed language, which means that the types of variables cannot be decided or changed at runtime.
Because your array of numbers are input at runtime, it's impossible for you to use the NthTypeOf metafunction in the manner you describe, because NthTypeOf can only depend on a compile-time index.
In your use case, not only are the variables of different type, but the behavior is also different based on user input.
If you want different behavior based on a value determined at runtime, I suggest either a switch statement, a container of std::function, or a heterogeneous container of polymorphic "command" objects.
A solution based on a switch statement is pretty trivial, so I won't bother showing an example.
A std::function is a polymorphic wrapper around a function-like object. You can use a container of std::function to build a sort of dispatch table.
struct StringMatch
{
void operator()() const
{
std::string s1, s2;
std::cin >> s1 >> s2;
if (s1 == s2)
std::cout << "Strings match\n";
else
std::cout << "Strings don't match\n";
}
};
struct SquareRoot
{
void operator()() const
{
float x = 0;
std::cin >> x;
std::cout << "Square root is " << std::sqrt(x) <<"\n";
}
};
int main()
{
const std::map<int, std::function> commands =
{
{1, StringMatch()},
{2, SquareRoot()},
};
int commandId = 0;
std::cin >> commandId;
auto found = command.find(commandId);
if (found != commands.end())
(*found->second)();
else
std::cout << "Unknown command";
return 0;
}
The map can of course be replaced by a flat array or vector, but then you need to worry about "holes" in the command ID range.
If you need your command objects to be able to do more then execute themselves (like having properties, or support undo/redo), you can use a solution that uses polymorphism and is inspired by the traditional Command Pattern.
class Command
{
public:
virtual ~Command() {}
virtual void execute();
virtual std::string name() const;
virtual std::string description() const;
};
class StringMatch : public Command
{
public:
void execute() override
{
std::string s1, s2;
std::cin >> s1 >> s2;
if (s1 == s2)
std::cout << "Strings match\n";
else
std::cout << "Strings don't match\n";
}
std::string name() const override {return "StringMatch";}
std::string description() const override {return "Matches strings";}
};
class SquareRoot : public Command
{
public:
void execute() override
{
float x = 0;
std::cin >> x;
std::cout << "Square root is " << std::sqrt(x) <<"\n";
}
std::string name() const override {return "SquareRoot";}
std::string description() const override {return "Computes square root";}
};
int main()
{
constexpr int helpCommandId = 0;
const std::map<int, std::shared_ptr<Command>> commands =
{
{1, std::make_shared<StringMatch>()},
{2, std::make_shared<SquareRoot>()},
};
int commandId = 0;
std::cin >> commandId;
if (commandId == helpCommandId)
{
// Display command properties
for (const auto& kv : commands)
{
int id = kv.first;
const Command& cmd = *kv.second;
std::cout << id << ") " << cmd.name() << ": " << cmd.description()
<< "\n";
}
}
else
{
auto found = command.find(commandId);
if (found != commands.end())
found->second->execute();
else
std::cout << "Unknown command";
}
return 0;
}
Despite C++ being a statically-typed language, there are ways to emulate Javascript-style dynamic variables, such as the JSON for Modern C++ library or Boost.Variant.
Boost.Any can also be used for type erasure of your command arguments, and your command objects/functions would know how to downcast them back to their static types.
But such emulated dynamic variables will not address your need to have different behavior based on user/file input.
One possible approach when you want to do something with a run-time dependent type very locally, is to predict run-time values at the compile time.
using Tuple = std::tuple<int, double, char>;
int type;
std::cin >> type;
switch(type) {
case 0: {
using ItsType = std::tuple_element<0, Tuple>;
break;
}
case 1: {
using ItsType = std::tuple_element<1, Tuple>;
break;
}
default: std::cerr << "char is not handled yet." << std::endl;
break;
}
Only works with small type packs, of course.
Is there a way to access Nth type at runtime?
Yes, although per other answers, it may not be appropriate in this context.
Adapting this answer, you can iterate at compile time, and choose a type.
#include <iostream>
#include <fstream>
#include <string>
#include <type_traits>
#include <tuple>
#include <cmath>
std::ifstream in("my.txt");
void do_something(const std::string& x)
{
std::cout << "Match " << x << '\n';
}
void do_something(int x)
{
std::cout << "Sqrt of " << x << " = " << std::sqrt(x) << '\n';
}
template<std::size_t I, typename... Tp>
inline typename std::enable_if_t<I == sizeof...(Tp)> action_on_index_impl(size_t)
{ // reached end with I==number of types: do nothing
}
template<std::size_t I, typename... Tp>
inline typename std::enable_if_t<I < sizeof...(Tp)> action_on_index_impl(size_t i)
{
if (i == I){
// thanks to https://stackoverflow.com/a/29729001/834521 for following
std::tuple_element_t<I, std::tuple<Tp...>> x{};
in >> x;
do_something(x);
}
else
action_on_index_impl<I+1, Tp...>(i);
}
template<typename... Tp> void action_on_index(size_t i)
{
// start at the beginning with I=0
action_on_index_impl<0, Tp...>(i);
}
int main()
{
int i{};
while(in >> i, in)
action_on_index<std::string, int>(i);
return 0;
}
with my.txt
0 hello
1 9
0 world
1 4
output
Match hello
Sqrt of 9 = 3
Match world
Sqrt of 4 = 2
I needed to know how to access Nth type at runtime in a different context, hence my answer here (I wonder if there is a better way, particularly in C++14/17).

Something like "if constexpr" but for class definition

if constexpr is a big step for getting rid of preprocessor in C++ programs. However it works only in functions - like in this example:
enum class OS
{
Linux,
MacOs,
MsWindows,
Unknown
};
#if defined(__APPLE__)
constexpr OS os = OS::MacOs;
#elif defined(__MINGW32__)
constexpr OS os = OS::MsWindows;
#elif defined(__linux__)
constexpr OS os = OS::Linux;
#else
constexpr OS os = OS::Unknown;
#endif
void printSystem()
{
if constexpr (os == OS::Linux)
{
std::cout << "Linux";
}
else if constexpr (os == OS::MacOs)
{
std::cout << "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
std::cout << "MS Windows";
}
else
{
std::cout << "Unknown-OS";
}
}
But dreams about getting rid of preprocessor are not quite satisfied - because the following examples do not compile:
1 Cannot use it in class definition to define some members of class differently:
class OsProperties
{
public:
static void printName()
{
std::cout << osName;
}
private:
if constexpr (os == OS::Linux)
{
const char* const osName = "Linux";
}
else if constexpr (os == OS::MacOs)
{
const char* const osName = "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
const char* const osName = "MS Windows";
}
else
{
const char* const osName = "Unknown";
}
};
2 Nor it works for not class-scope (like global scope):
if constexpr (os == OS::Linux)
{
const char* const osName = "Linux";
}
else if constexpr (os == OS::MacOs)
{
const char* const osName = "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
const char* const osName = "MS Windows";
}
else
{
const char* const osName = "Unknown";
}
I am (almost) sure this is per C++17 specification that if constexpr works only within function bodies - but my questions are:
Q1 How to achieve the similar effect like if-constexpr in functions - for class and global scope in C++1z/C++14? And I am not asking here for yet another explanation of template specialization... But something that has similar simplicity as if constexpr...
Q2 Are there any plan to extend C++ for the above mentioned scopes?
How to achieve the similar effect like if-constexpr in functions - for class and global scope in C++1z/C++14? And I am not asking here for yet another explanation of template specialization...
You basically just said, "I want template specialization, but without all that pesky template specialization."
if constexpr is the tool for making the behavior of functions change based on compile-time constructs. Template specialization is the tool that C++ provides for making definitions change based on compile-time constructs. It is the only tool C++ provides for this functionality.
Now for your simplistic case of initializing a variable, you can always create and call a lambda. C++17 offers constexpr support for lambdas, and a lambda would be able to use if constexpr to decide what value to return.
Are there any plan to extend C++ for the above mentioned scopes?
No. Here are all of the proposals, and none of the ones from the past couple of years delve into this domain.
And it's highly unlikely they ever will.
An index type:
template<std::size_t I>
using index = std::integral_constant<std::size_t, I>;
first_truth takes a set of compile-time bools and says what the index of the first one is at compile time. If you pass it N compile-time bools, it returns N if all are false:
constexpr index<0> first_truth() { return {}; }
template<class...Rest>
constexpr index<0> first_truth(std::true_type, Rest...) { return {}; }
template<class...Rest>
constexpr auto first_truth(std::false_type, Rest...rest) {
return index<first_truth( rest... )+1>{};
}
dispatch takes a set of compile-time bools and returns a lambda. This lambda returns via perfect forwarding the first element that matches the first true compile time bool:
template<class...Bools>
constexpr auto dispatch(Bools...bools) {
constexpr auto index = first_truth(bools...);
return [](auto&&...fs){
return std::get< decltype(index){} >(
std::forward_as_tuple( decltype(fs)(fs)... )
);
};
}
A compile time bool type:
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<bool b>
bool_t<b> bool_k{};
Now we solve your problem:
const char* const osName =
dispatch(
bool_k<os == OS::Linux>,
bool_k<os == OS::MacOs>,
bool_k<os == OS::MsWindows>
)(
"Linux",
"MacOS",
"MS Windows",
"Unknown"
);
which should approximate a compile-time switch. We could tie the bools more closely to the arguments with a bit more work.
Code not compiled, probably contains tpyos.
how to define different types based on some compile time constant w/o template specialization?
Here it is:
constexpr auto osPropsCreate()
{
if constexpr (os == OS::Linux) {
struct Props { const char* name; int props1; using handle = int; };
return Props{"linux", 3};
} else if constexpr (os == OS::MacOs) {
struct Props { const char* name; using handle = float; };
return Props{"mac"};
} else if constexpr (os == OS::MsWindows) {
struct Props { const char* name; using handle = int; };
return Props{"win"};
} else
return;
}
using OsProps = decltype(osPropsCreate());
constexpr OsProps osProps = osPropsCreate();
As you can see - I used the new construction if constexpr to produce from some "implementation" function the type that depends on compile time constant. It is not as easy to use as static if in D language - but it works - I can do it:
int linuxSpecific[osProps.props1];
int main() {
std::cout << osProps.name << std::endl;
OsProps::handle systemSpecificHandle;
}
Next thing - define different functions depending on compile time constant:
constexpr auto osGetNameCreate() {
if constexpr (os == OS::Linux) {
struct Definition {
static constexpr auto getName() {
return "linux";
}
};
return Definition::getName;
} else if constexpr (os == OS::MacOs) {
// we might use lambda as well
return [] { return "mac"; };
} else if constexpr (os == OS::MsWindows) {
struct Definition {
static constexpr auto getName() {
return "win";
}
};
return Definition::getName;
} else
return;
}
constexpr auto osGetName = osGetNameCreate();
int main() {
std::cout << osGetName() << std::endl;
}
Actually, they can be either function-like objects (functors) or static member functions from nested classes. This does not matter - one have full freedom to define different things for different compile time constants (OS type in this case). Notice, that for unknown system we just return void - it will cause compilation error for unknown system...
Answering to second question:
The first answer provide it with reasoning in comments (link). My interpretation is that C++ standard committee is not ready for that change. Maybe competing with D will/would be a good reason to raise this subject once again...

Template casting issue

I seem to be getting an error in the below code when I attempt to cast to a template of class T, when T is of type float. I have realized already that a type of int functions correctly, because the following is valid syntax:
char* str = "3";
int num = (int)str;
The same is not true of float. I'm wondering if there is a way to stop the g++ compiler erroring on a type mismatch so I can handle it with the RTTI method typeid().
class LuaConfig {
// Rest of code omitted...
// template currently supports both string and int
template <class T> T getC(const char *key) {
lua_pushstring(luaState, key);
lua_gettable(luaState, -2);
if (!lua_isnumber(luaState, -1)) {
// throw error
std::cout << "NOT A NUMBER" << std::endl;
}
T res;
// WHERE THE PROBLEM IS:
if ( typeid(T) == typeid(int)
|| typeid(T) == typeid(float)
) {
std::cout << "AS NUM" << std::endl;
// Floats should fall in here, but never does because of the
// else clause failing at compile time.
res = (T)lua_tonumber(luaState, -1);
} else {
// TODO: Fails on float here, it should fall down the
// first branch (above). This branch should only be for string data.
std::cout << "AS STRING" << std::endl;
res = (T)lua_tostring(luaState, -1); // LINE THAT CAUSES ISSUE.
}
std::cout << "OUT:" << res << std::endl;
lua_pop(luaState, 1);
return res;
}
}
int main( int argc, char* args[] ) {
LuaConfig *conf = new LuaConfig();
std::cout << conf->getC<int>("width") << std::endl;
std::cout << conf->getC<float>("width") << std::endl; // This causes the error.
}
The error g++ throws is:
source/Main.cpp:128: error: invalid cast from type ‘char*’ to type ‘float’
Try to avoid C-style casts. If you write (int)ptr where ptr is some pointer this will be a reinterpret_cast which is probably not what you want. For converting numbers to strings and back again check various FAQs. One way to do this is to use the std::stringstream class.
A C-style cast is dangerous because it can be used for lots of things and it's not always apparent what it does. C++ offers alternatives (static_cast, dynamic_cast, const_cast, reinterpret_cast) and a functional-style cast which is equivalent to a static cast).
In the case of (int)ptr it converts the pointer to an int and not the string representation of a number the pointer points to.
You might also want to check out Boost's lexical_cast.
Edit: Don't use typeid for this. You can handle this completely at compile-time:
template<typename T> struct doit; // no definition
template<> struct doit<int> {
static void foo() {
// action 1 for ints
}
};
template<> struct doit<float> {
static void foo() {
// action 2 for floats
}
};
....
template<typename T> void blah(T x) {
// common stuff
doit<T>::foo(); // specific stuff
// common stuff
}
In case T is neither int nor float you get a compile-time error. I hope you get the idea.
You need branching at compile time. Change the content in your template to something like this:
template<typename T> struct id { };
// template currently supports both string and int
template <class T> T getC(const char *key) {
lua_pushstring(luaState, key);
lua_gettable(luaState, -2);
if (!lua_isnumber(luaState, -1)) {
// throw error
std::cout << "NOT A NUMBER" << std::endl;
}
T res = getCConvert(luaState, -1, id<T>())
std::cout << "OUT:" << res << std::endl;
lua_pop(luaState, 1);
return res;
}
// make the general version convert to string
template<typename T>
T getCConvert(LuaState s, int i, id<T>) {
return (T)lua_tostring(s, i);
}
// special versions for numbers
float getCConvert(LuaState s, int i, id<int>) {
return (float)lua_tonumber(s, i);
}
int getCConvert(LuaState s, int i, id<float>) {
return (int)lua_tonumber(s, i);
}
There are a couple of alternative ways to solve this. To avoid repeatedly adding overloads, boost::enable_if could be useful. But as long as you have only two special cases for int and float, i would keep it simple and just repeat that one call to lua_tonumber.
Another pattern that avoids enable_if and still avoids repeating the overloads is to introduce a hierarchy of type flags - change id to the following, and keep the code within getC the same as above. I would use this if there tends to be more cases that need special handling:
template<typename T> struct tostring { };
template<typename T> struct tonumber { };
template<typename T> struct id : tostring<T> { };
template<> struct id<int> : tonumber<int> { };
template<> struct id<float> : tonumber<float> { };
id needs to be defined outside the class template now, because you cannot explicitly specialize it within the template. Then change the overloads of the helper function to the following
// make the general version convert to string
template<typename T>
T getCConvert(LuaState s, int i, tostring<T>) {
return (T)lua_tostring(s, i);
}
// special versions for numbers
template<typename T>
T getCConvert(LuaState s, int i, tonumber<T>) {
return (T)lua_tonumber(s, i);
}
The specializations would then determine the "configuration" of what should use strings and what number conversion.
I'm not familiar with Lua, but I don't think it matters in this case...
The return of lua_toString is clearly a char* which means that you're getting the address of the value and then attempting to convert that address to a float. Have a look at strtod to see how to do this more correctly or, as sellibitze noted, use a stringstream.
I never touched lua or its engine before, but it seems to me that you are misusing lua_tostring. This is its signature:
const char *lua_tostring (lua_State *L, int index);
Obviously, this function returns a const char*. In case of T == int, C/C++ allow what is called reinterpret_cast from a pointer into int. This conversion is meaningless in case of T == float. I think you have to take the returned c-string, then convert it into a number using atoi or atof depending on the type. The problem happens here:
res = (T)lua_tonumber(luaState, -1);
Because as we said, pointers can be converted into integers in C/C++ in a meaningful way, unlike floats.
Using memcpy() to make the assignment will avoid the compiler error.
char *str = lua_tostring(luaState, -1)
memcpy(&res, &str, sizeof(res));
However, the string returned by lua_tostring() is no longer valid after the call to lua_pop(). The string really needs to be copied into another buffer.
Even though the Lua website says that, and it makes logical sense, when testing I found that the pointer remained valid even after the state was closed. While he's right that you really should copy the string if you want to keep it around after your Lua function returns, it's probably not his problem.