2

Suppose I have the following very simple class:

class A
{
public:
    static constexpr A make() { return A{}; }

    constexpr A() : _v(0) {}

    constexpr A& setV(int v) { _v = v; return *this; }

private:
    int _v;
};

If I try to use it as follows:

int main()
{
    volatile A a1;
    a1.setV(10);

    //OR

    //The optimizer will optimize this chain of constexpr calls into a single "store" instruction
    a1 = A::make().setV(10); //More chaining here

    return 0;
}

The code will not compile.

I understand why this is true based upon: Defining volatile class object

I know that the solution would be to add an additional method like so:

class A
{
public:
    constexpr A() : _v(0) {}

    volatile A& setV(int v) volatile { _v = v; return *this; }
    constepxr A& setV(int v) { _v = v; return *this; }

private:
    int _v;
};

As an aside, I am aware that returning the volatile reference will issue a warning if it is not used.

My question is, is there any way I can avoid code duplication in this case? Both implementations of setV will always be the same. Thus, if I add/change more methods, I have to maintain them both which could get messy if they aren't so trivial. The only difference is one of type qualifiers...

Since there have been some mentions of this in the comments, I thought I would note that I need volatile here because I am attempting to eventually implement "class overlay" for hardware peripheral register access on an embedded system.

Patrick Wright
  • 1,401
  • 7
  • 13
  • 2
    do you really need `volatile`? Its uses are fairly niche – Alan Birtles Dec 14 '22 at 21:11
  • 3
    Most classes don't overload for `volatile`. A `volatile` variable is a variable where the underlying hardware may change the value of the object unbeknownst to the program so we use `volatile` to tell the compiler about this so it always loads the current value. Class objects typically aren't something that is mapped to hardware where the hardware is changing it outside the control of the program. – NathanOliver Dec 14 '22 at 21:12
  • 1
    @AlanBirtles Yes, my actual application is on an embedded system to access hardware registers. – Patrick Wright Dec 14 '22 at 21:12
  • @NathanOliver In my case, I actually am accessing hardware. I am trying to implement the "class overlay" technique to abstract access to the hardware registers via class members. – Patrick Wright Dec 14 '22 at 21:13
  • NathanOliver is saying "`a1` doesn't need to be volatile. Only `_v` does" – Mooing Duck Dec 14 '22 at 21:14
  • 2
    @PatrickWright Then you want volatile members in your class, not a volatile class – NathanOliver Dec 14 '22 at 21:15
  • Whether or not it resulted in a warning is irrelevant. – Jesper Juhl Dec 14 '22 at 21:16
  • @NathanOliver You caught me... I modified the question to include the additional, key reason why I need to whole class to be volatile. That is, to use the "Register" class at constepxr time to compute the complete value of a register at compile-time whilst still being able to use the helper methods. I apologize for not including this initially. – Patrick Wright Dec 14 '22 at 21:21
  • @MooingDuck I think I need to ask the question, it "this" is volatile (i.e., volatile A* this) then does that mean access to this->_v are also volatile? In other words, does _v become implicitly volatile in the context of the volatile member method? – Patrick Wright Dec 14 '22 at 21:31
  • In answer to my own question above: https://stackoverflow.com/questions/4826719/c-volatile-member-functions – Patrick Wright Dec 15 '22 at 15:34

2 Answers2

7

In C++23, you can use an explicit object parameter (also known as deducing this) for this purpose:

class A
{
public:
    A() : _v(0) {}
    template <class Self>
    constexpr auto&& setV(this Self&& self, int v) {
        self._v = v; return self;
    }
private:
    int _v;
};

Unfortunately, as of this writing the only compiler that supports this is the latest version of Microsoft Visual C++.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • 1
    Even if it's not particularly useful at this moment, this is still very interesting. And in 10 years when embedded compilers adopt C++23, I can use it! – Patrick Wright Dec 14 '22 at 21:23
0

I came up with one possible solution. It's dubious whether it's less verbose than what I had before, but at least I don't have to duplicate the body of the method. In fact, it's similar (in my mind) to the proposed C++23 answer by @ComicSansMS.

class A
{
public:
    constexpr A() : _v(0) {}

    template <typename T>
    static constexpr void setV(T& dest, int src) { dest = src; }

    volatile A& setV(int v) volatile { setV(_v, v); return *this; }
    constepxr A& setV(int v) { setV(_v, v); return *this; }

private:
    int _v;
};

T will be deduced as either int& or volatile int& depending on the context. Since its declare constexpr/inline, the compiler/optimizer still boils it down to a few assembly instructions.

Patrick Wright
  • 1,401
  • 7
  • 13