Reputation: 78633
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:
Type var = { expr, expr };
syntax.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
?
Upvotes: 6
Views: 583
Reputation: 39141
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>();
}
Upvotes: 1
Reputation: 218098
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
.
Upvotes: 1