Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper setter and getter for static member variable in header-only library

I have a few small header-only libraries (the header-only part is important). In the initial versions, I had some static members in the classes defined therein. It didn't occur to me until later (when I used them in a bigger project) that the static members would violate the ODR. I wanted to keep them header-only, so defining the static members in a separate .cpp file was out of the question. One well-known solution is to use a Meyers singleton function-local static variable for each static member (as suggested, for example, here).

That is all well and good, but since I want the singleton to behave like a member variable, I want to be able to get and set the value using setters and getters. But what do getters and setters for Meyers singletons function-local static variables look like? I haven't been able to find any solutions to that particular problem.

To clarify, these are the requirements:

  • I want the behaviour of a static member variable inside a header-only library (so I cannot put definitions in a .cpp file)
  • I want a getter which is a getter only (I shouldn't be able to modify the value by assigning to the reference returned by the getter)
  • I also want to be able to modify the value via a dedicated setter.

EDIT 1:

I would like to explain why you might need this.

The static variables in the libraries I mentioned define the default values for some parameters. However, rather than hard-coding these default, I want to give the user the option to set the default values at the beginning of the program so they don't have to pass the values manually each time they call a member function or construct a new instance.

Also, although I agree that the use of the term "Meyers singleton" in the example provided here is misleading (I'm just using an int value), there is nothing stopping you from using this paradigm with custom classes which you only want a single instance of. In such cases, the "Meyers singleton" term would be justified.


EDIT 2:

This has become somewhat irrelevant with the introduction of inline static variables in C++17, but I'll leave it up for people who don't have the option to use C++17.

like image 957
cantordust Avatar asked Jan 30 '23 19:01

cantordust


1 Answers

Solution

#include <iostream>

class Foo
{

    private:

        static int& val()
        {
            static int v = 0;
            return v;
        }

    public:

        Foo()
        {
            set_val(14);
        }

        Foo(const int _v)
        {
            set_val(_v);
        }

        // The setter uses the fact that val()
        // returns a non-const reference,
        // so we can assign to it.
        static void set_val(const int _v)
        {
            val() = _v;
        }           

        // A true getter.
        // Returns const int&, so we cannot assign to it.
        static const int& get_val()
        {
            return val();
        }
};

int main(void)
{

    std::cout << "val is " << Foo::get_val() << "\n";
    Foo f1; // Set the value implicitly via an object constructor
    std::cout << "val is " << Foo::get_val() << "\n";
    Foo f2(5); // Set the value explicitly via an object constructor
    std::cout << "val is " << Foo::get_val() << "\n";
    Foo::set_val(42);
    std::cout << "val is " << Foo::get_val() << "\n";
    // Foo::get_val() = 4; // Doesn't compile, as required

    return 0;
}

Output:

val is 0
val is 14
val is 5
val is 42

Setting the value via constructors can (and probably should) be omitted. I just wanted to show that it can be done. This is a lot of code just for one variable, but not that much more than for a non-static member.

Any thoughts, comments and suggestions welcome!

like image 125
cantordust Avatar answered Feb 04 '23 17:02

cantordust