6

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?

Community
  • 1
  • 1
BCS
  • 75,627
  • 68
  • 187
  • 294
  • @Jefffrey: Depending on context you could be right or wrong. My belief is at least that you really need to be clever to imagine every possible practical case in which you want to balance flexibility of use vs restrictions of use. – user2672165 Mar 14 '14 at 10:27
  • What are the reasons behind these restrictions? – Shoe Mar 14 '14 at 10:28
  • @Jefffrey: imposed by policy. tl;dr; inter-compilation unit order of static initialization is unspecified. – BCS Mar 14 '14 at 18:17

2 Answers2

1

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • That results in calling the `constexpr` constructor to create a `const` variable which is the exact usage I want to prevent. – BCS Mar 14 '14 at 19:59
1

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>();
}
dyp
  • 38,334
  • 13
  • 112
  • 177
  • This might show one problem: By returning by-reference, you have to provide a definition for the variable, since it's ODR-used (IIRC). But that's not really what you want when using constant expressions.. – dyp Mar 15 '14 at 14:37
  • Hmmm you can actually make that static data member private and provide (`static constexpr`) accessor functions to it.. – dyp Mar 15 '14 at 14:46