Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ type that can only be used as constexpr variable

I have a library type that is only supposed to be used as a global variable and must be linker initialized (i.e. it must have the correct initial values before static initialization time). I have strong reason to think that I will get what I need if I do one of two things:

  1. make it a POD type, place the user provided members first and trust the user to pass the correct number of expressions to the Type var = { expr, expr }; syntax.
  2. make the implementation private, provide a constexpr constructor and depend on the user to declare all instances as constexpr.

Neither of these is nice in that it depends on the user not mucking things up.

Short of macro magic, is there any way to enforce that all instances of a type be constexpr?

like image 746
BCS Avatar asked Mar 13 '14 23:03

BCS


People also ask

What is the difference between const and constexpr variables?

One or more parameters, each of which must be a literal type and must itself be a constant expression. A constexpr variable or function must return a literal type. The primary difference between const and constexpr variables is that the initialization of a const variable can be deferred until run time.

What is the difference between constexpr and constexpr in C++ 14?

C++ 14 allows more than one statement. constexpr function should refer only to constant global variables. constexpr function can call only other constexpr function not simple function. The function should not be of a void type and some operators like prefix increment (++v) are not allowed in constexpr function.

What are the rules for constexpr functions in C++?

A constexpr function or constructor is implicitly inline. The following rules apply to constexpr functions: A constexpr function must accept and return only literal types. A constexpr function can be recursive. It cannot be virtual. A constructor cannot be defined as constexpr if the enclosing class has any virtual base classes.

What is a constexpr function in Python?

A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument. When its arguments are constexpr values, a constexpr function produces a compile-time constant.


Video Answer


2 Answers

Create a

template<Type1 value1, Type2 value2>
constexpr MyType make_MyTYpe(/* No args */)
{
    return MyType(value1, value2); // call the (private) constexpr constructor
}

And if your type provide only const method, user have to use const object.

const MyType myobject = make_MyType<4, 2>();

myobject is const from a constexpr.

like image 198
Jarod42 Avatar answered Nov 05 '22 04:11

Jarod42


What you cannot prevent is users declaring const& to any instances created. However, you can prevent copying and moving instances. Now you'll only need to enforce that all instances created are created in a context where a constant expression is required.

Here's a weird way to enforce this: Let all instances be static constexpr members of a class (template).

The user then provides a way to get the constructor parameters of your "variable".

struct constructor_params
{
    int i;
    double d;
};

The instance provided by the user has to be usable in a constant expression in order to initialize the static constexpr member.

In order to create different instances, we need some kind of tag value to create different instantiations of a class template that contains the static constexpr member that'll serve as variable instance. I chose to combine the tag value and the way of supplying the constructor_params parameter by letting the user provide a factory function or type to create the parameter.

First, the variable type you want only to have constexpr instances of:

// forward declarations useful for friendship
template<class T>
struct make_variable_by_type;

template<constructor_params(*fptr)(void)>
struct make_variable_by_func;


struct the_variable
{
    the_variable(the_variable const&) = delete;
    the_variable(the_variable&&) = delete;

private:
    constexpr the_variable(constructor_params) {}

    template<class T>
    friend struct make_variable_by_type;

    template<constructor_params(*fptr)(void)>
    friend struct make_variable_by_func;
};

In order to let the user access both ways to create a variable with one name, there is an overloaded make_variable function:

template<constructor_params(*fptr)(void)>
struct make_variable_by_func
{
    static constexpr the_variable value{fptr()};
};
template<constructor_params(*fptr)(void)>
const the_variable make_variable_by_func<fptr>::value;

template<class T>
struct make_variable_by_type
{
    static constexpr the_variable value{T::make()};
};
template<class T>
const the_variable make_variable_by_type<T>::value;


template<class T>
constexpr the_variable const& make_variable()
{
    return make_variable_by_type<T>::value;
}

template<constructor_params(*fptr)(void)>
constexpr the_variable const& make_variable()
{
    return make_variable_by_func<fptr>::value;
}

Now, two usage examples. One with a constexpr function to create the constructor_params and one with a local type (the function scope is the reason why the creation from type is necessary).

constexpr constructor_params make_my_variable()
{
    return {42, 21.0};
}

constexpr auto& x = make_variable<make_my_variable>();

int main()
{
    struct make_my_other_variable
    {
        static constexpr constructor_params make()
        {
            return {1, 2};
        }
    };

    constexpr auto& x = make_variable<make_my_other_variable>();
}
like image 21
dyp Avatar answered Nov 05 '22 05:11

dyp