24
struct FOO{
    int a;
    int b;
    int c;
};

volatile struct FOO foo;

int main(void)
{
    foo.a = 10;
    foo.b = 10;
    foo.c = 10;
    struct FOO test = foo;

    return 0;
}

This won't compile, because struct FOO test = foo; generates an error:

error: binding reference of type 'const FOO&' to 'volatile FOO' discards qualifiers

How can I copy a volatile struct into another struct in C++ (before C++11)?

Many people suggested to just delelte volatile, but I can't do that in that case, because I want to copy the current SPI-Reg setttings inside a µC and this is declared volatile by the manufacturer headers. I want to copy those settings, because the manufactuerer also provides an Library to use the SPI for EnDat-Communication, and I don't have access to the source-code. Since I have to change the SPI-Reg-Settings during runtime I want to easyly get back to the library SPI-settings without calling the init_endat()-lib fkt again (it's unspecified what happens if i call it twice).

Could I possibly use memcopy() for that?

As suggested, this is a copy of the following question.

Why am I not provided with a default copy constructor from a volatile?

makum
  • 243
  • 2
  • 7
  • 13
    Perhaps the first question is what do you want volatile for? (hint: you probably don't) – UKMonkey Mar 19 '18 at 16:29
  • 3
    The odds are approximately 100% that using `volatile` is a mistake. Are you dealing with memory mapped hardware? I didn't think so. Remove `volatile` from your vocabulary. – Jive Dadson Mar 19 '18 at 17:39
  • 6
    @JiveDadson There are many other valid uses of volatile (performance testing, mathematical computations, signal handlers just to name a few). I'm not arguing that the OP does one of these - just against the idea that volatile has no use outside low-level operations with hardware. – martinkunev Mar 19 '18 at 19:32
  • Which compiler gives you this error ? Also, using "volatile" is useful when clearing memory that contains some sensitive data (e.g. encryption keys) even if no code reads this memory after clearing. See: https://stackoverflow.com/questions/49084430/using-volatile-twice-in-an-r-value. – George Robinson Mar 20 '18 at 01:50
  • I think memcpy will suffer from the same error. Perhaps copying it memberwise is the only option. – n. m. could be an AI Mar 20 '18 at 06:53
  • Possible duplicate (sort of) https://stackoverflow.com/questions/17217300/why-am-i-not-provided-with-a-default-copy-constructor-from-a-volatile – n. m. could be an AI Mar 20 '18 at 06:59
  • @n.m. I tried it, and if u typecast the Adress of the volatile instance to (const void*) it works. (if it is a good sulotion is another question) memcpy(&struct_to_copy_to, (const void*)&struct_that_is_volatile, sizeof(struct_that_is_volatile)); – makum Mar 20 '18 at 08:32
  • @makum it will of course compile with a cast, but also a normal copy ctor will compile with a cast. Strictly speaking though it's UB. – n. m. could be an AI Mar 20 '18 at 09:02
  • I literally came here trying to find an answer to a similar question, working on embedded development, using volatile to interact with memory mapped hardware. And the first thing I see is @JiveDadson being a condescending prick telling the OP he's clearly wrong for using volatile. This site is a cesspool. – jtdubs Jul 09 '23 at 23:42

3 Answers3

24

This is ill-formed because FOO has an implicit copy constructor defined as:

FOO(FOO const&);

And you write FOO test = foo; with foo of type volatile FOO, invoking:

FOO(volatile FOO const&);

But references-to-volatile to references-to-non-volatile implicit conversion is ill-formed.

From here, two solutions emerge:

  1. don't make volatile to non-volatile conversions;
  2. define a suited copy constructor or copy the object members "manually";
  3. const_cast can remove the volatile qualifier, but this is undefined behavior to use that if your underlying object is effectively volatile.

Could I possibly use memcopy() for that?

No you cannot, memcpy is incompatible with volatile objects: thre is no overload of it which takes pointers-to-volatile, and there is nothing you can do without invoking undefined behavior.

So, as a conclusion, your best shot if you cannot add a constructor to FOO is to define:

FOO FOO_copy(FOO volatile const& other)
{
    FOO result;
    result.a = other.a;
    result.b = other.b;
    result.c = other.c;
    return result;
}

Or with C++11's std::tie:

FOO FOO_copy(FOO volatile const& other)
{
    FOO result;
    std::tie(result.a, result.b, result.c) = std::tie(other.a, other.b, other.c);
    return result;
}
YSC
  • 38,212
  • 9
  • 96
  • 149
  • You omitted the only correct answer: 0. Remove `volatile`. – Jive Dadson Mar 19 '18 at 17:40
  • 13
    @JiveDadson it's kinda included in 1. ;) though, `volatile` might be really needed in OP's context, we don't know. But we know from [OP's profile page](https://stackoverflow.com/users/9262651/makum) they are an _embedded_ software engineer, so... – YSC Mar 19 '18 at 18:01
  • I guess it's possible. I had a use for volatile about 23 years ago. It can happen. – Jive Dadson Mar 19 '18 at 18:06
  • You say "two solutions", then provide a list of three items. I think the list item shouldn't be part of the list (since it's not a solution). – Toby Speight Mar 19 '18 at 18:16
  • @TobySpeight that's the joke. Its funny because the list does actually contain exactly two solutions, is it not? – YSC Mar 19 '18 at 18:17
  • It seems to me like the issue here is that the C++ implicit copy constructor uses a reference to the source value, right? There is no fundamental reason why it could not instead read the value of the source struct and then use that as a value and write that value to the destination struct without violating the semantics of the volatile, right? – bigjosh Mar 19 '23 at 07:10
  • It looks like the correct answer is "you cannot do this because of the way the standard choose to implement the copy constructor".... https://stackoverflow.com/a/17218983/3152071 – bigjosh Mar 20 '23 at 02:58
21

To give another approach to an answer, to address why this doesn't make sense, rather than just where the C++ standard says this is invalid:

The whole point of volatile is that you have precise control over which variable gets accessed when. That means given volatile int i, j;, i = 1; j = 2; and j = 2; i = 1; do not do the same thing. The compiler cannot freely transform one into the other. The same applies to reads: given volatile int i, j; int x, y;, x = i; y = j; and y = j; x = i; do not do the same thing. The presence of volatile means the accesses must happen in exactly the order you specified.

Now, in your example, what should struct FOO test = foo; do? You've never specified whether you want to first read foo.a, then foo.b, finally foo.c, or perhaps first read foo.c, then foo.b, finally foo.a, or perhaps some other order.

You can, if you wish, do this:

struct FOO test;
test.a = foo.a;
test.b = foo.b;
test.c = foo.c;

Here, you explicitly specify the order of the accesses to foo's fields, so you avoid the problem.

  • 1
    I like this approach. – YSC Mar 19 '18 at 16:44
  • 4
    That's not what `volatile` means. That's what std::atomic with memory order std::memory_order_seq_cst means. Visual C++ makes some memory order guarantees for volatile unless you set a standard-compliance flag, but the promises are not that strong. `volatile` is for accessing memory-mapped hardware only and I guess UNIX signal handlers. – Jive Dadson Mar 19 '18 at 17:50
  • 5
    @JiveDadson You're reading more into my answer than what I actually wrote. What I wrote *is* exactly what `volatile` means. Per [intro.execution]p8.1: "Access to volatile objects are evaluated strictly according to the rules of the abstract machine." –  Mar 19 '18 at 18:39
  • 1
    @hvd `volatile` guarantees individual accesses will not be optimized away, but does not prevent them from being reordered with others (that is, `volatile` in C++ does not establish happens-before order). – JAB Mar 19 '18 at 19:44
  • 11
    I like how `volatile` is sooo mystical for some developers :) @JAB you know how the compiler is allowed to reorder instruction following the "as-if" rule? Well this rule does not apply for volatile objects (see `[intro.execution]/8.1` quoted above). What you are talking about is related to multi-threaded programs, and yes in this context `volatile` does not help. hvd's answer is about in-thread instruction order. – YSC Mar 19 '18 at 19:59
  • @YSC It's been a while since I delved into the nitty-gritty bits so I'll yield to you on that. – JAB Mar 19 '18 at 20:06
  • 1
    @JAB volatile is rarely useful and is misused too often, when in doubt, it's better not to use it than have a false sense of security, in that you're right. – YSC Mar 19 '18 at 20:09
  • I can do ` typedef long long twolongs; volatile twolongs x = 1; volatile twolongs y = x;` on a 32-bit machine no problem, even though that assignment necessarily is equivalent to if `twolongs` were defined with `struct twlongs {long a; long b};` so this is not about needing to specifically sequence assignments of internal non-atomic data. This is about the way the struct copy constructor is defined. They *could* have done it a way that was compatible with volatile semantics. – bigjosh Mar 20 '23 at 03:18
2

You haven't provided enough details about your problem to give a more precise assessment, but the solution to whatever problem you're trying to solve is almost certainly not to use volatile. "Volatile" means that the value can change from under your feet: the two typical good use cases are variables changed from within UNIX signal handlers and memory-mapped registers. Volatile is not enough for variables shared among threads, notably.

The reason you are getting this error is that your compiler is trying to find a FOO(volatile FOO&) copy constructor, which is never automatically generated.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • 3
    The other use case of `volatile` is for variables that are used in the presence of `setjmp` and `longjmp`. Which I would caution against using without a very, very good reason. – Eljay Mar 19 '18 at 18:36