Problem with gnu++11

Go To Last Post
29 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When I try to copy one instance of a class to another instance of the same class, the compiler has a hissy fit.  It occurs when the object in the class is a bitfield and is volatile.  One of the error messages I find humorous.  It mentions a const volatile.  I've never seen one of those before.

 

Here's a simple project.  It consists of main.cpp only.  I define the simple class, create 2 instances of it, and try to copy one to the other using the good old assignment operator '='.  It only fails if I use gcc with the -std=gnu++11.  It only fails if I declare the data member to be volatile and a bitfield. 

 

I have seen this problem before, or something similar, with gcc.  Motorola's compiler sees no problem.

 

Here is main.cpp and also the project template.

 


// This program copies one instance of a class to another instance of the same class.
// Gcc with -std=gnu++11 fails if the object in the class is a bitfield and is volatile.

// If I remove the -std=gnu++11 from the project, it compiles okay.
// Microsoft's compiler thinks it's okay too.  This makes 3 of us. ;)

// I'm using Atmel Studio 6.2.
// I put -std=gnu++11 in the compiler options Miscellaneous category.


class Class_with_volatile   {

   struct Control   {
      unsigned char software                    : 1;
      unsigned char reserved                    : 7;
      };

//  If you comment out the following volatile and uncomment the non-volatile, it compiles okay.

   volatile Control control;           // This doesn't work with gcc ++11.
//          Control control;           // This works.
   };


int main(void)
{
    Class_with_volatile class_with_volatile_1;
    Class_with_volatile class_with_volatile_2;

    class_with_volatile_2 = class_with_volatile_1;  // Won't compile with -std=gnu++11 and volatile bitfield object.


    while(1)
    {
    volatile int count;
    count++;
    }
}

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is this what you get:

avr.cpp:11:7: note: 'Class_with_volatile& Class_with_volatile::operator=(const Class_with_volatile&)' is implicitly deleted because the default definition would be ill-formed:
 class Class_with_volatile   {
       ^
avr.cpp:11:7: error: no matching function for call to 'Class_with_volatile::Control::operator=(const volatile Class_with_volatile::Control&) volatile'
avr.cpp:11:7: note: candidates are:
avr.cpp:13:11: note: Class_with_volatile::Control& Class_with_volatile::Control::operator=(const Class_with_volatile::Control&)
    struct Control   {
           ^
avr.cpp:13:11: note:   no known conversion for argument 1 from 'const volatile Class_with_volatile::Control' to 'const Class_with_volatile::Control&'
avr.cpp:13:11: note: Class_with_volatile::Control& Class_with_volatile::Control::operator=(Class_with_volatile::Control&&)
avr.cpp:13:11: note:   no known conversion for argument 1 from 'const volatile Class_with_volatile::Control' to 'Class_with_volatile::Control&&'

The error there is self-explanatory isn't it? but the very first "note:" sounds interesting. I guess one needs to understand in what way the compiler thinks it is "ill-formed".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not self-explanatory here.

 

The last error msg is particularly interesting. 

avr.cpp:13:11: note:   no known conversion for argument 1 from 'const volatile Class_with_volatile::Control' to 'Class_with_volatile::Control&&'

I must have missed the chapter in K&R that explained const volatile.

Last Edited: Mon. Apr 11, 2016 - 04:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

const volatile is not contradictory.  IIRC K&R does mention it.

 

I think that the issue is that there is no default assignment operator to a volatile class object.

The need for one might be a standard change.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

I think that the issue is that there is no default assignment operator to a volatile class object.

Not quite.   I think volatile class objects are okay as long as they don't contain bit fields.  I guess I didn't test this.

 

I guess in the general case it could be tricky (or impossible) to copy volatile bit fields and get consistent results every time.  In this case though it is easy.  Just copy the byte.

 

My question is this.  Are all these error messages just the compiler's way of saying "I'm buggy" or is this the wave of the future?  If it is buggy, I will wait for it to be fixed before using C++11.  Otherwise I will copy these classes by using something like memcpy.

 

I copy the peripheral register classes to messages and send them to the PC for display. The registers are all volatile and many contain bit fields.  If once in a million times I get a register contents that isn't consistent, it is no big deal.  I don't want to use copy constructors etc. for this.  Way to much code bloat.

 

If this compiler isn't buggy then the previous version is and so is Microsoft's.

 

  

 

 

 

Last Edited: Mon. Apr 11, 2016 - 06:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just tested it.  My guess was correct.  Volatile ints are no problem.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Apr 11, 2016 - 07:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah Joey, I get it.  For instance some functions that copy memory from one place to another will declare the source to be constant, or so I think.  That way if I mistakenly try to change the source, the compiler will kick me in the azz. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:
I just tested it.  My guess was correct.  Volatile ints are no problem.
An int is not a class object.

class_with_volatile_2.control is.

 

I think the messages would be easier to read if Control were defined outside of Class_with_volatile.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

An int is not a class object.

I made it a member of the class.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

I think the messages would be easier to read if Control were defined outside of Class_with_volatile.

I may try that tomorrow.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:
skeeve wrote:

An int is not a class object.

I made it a member of the class.
Doesn't matter.

The issue is a volatile class member of a class.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tested the simpler case where the bitfields are not inside a class.  All 3 compilers failed.  (gnu99, gnu++11, and Microsoft).  This doesn't surprise me. Years ago I been there and done that.  I didn't get a T shirt but I do have the scars.

 

All the compilers complain there is no match for the 'operator=' for operand types 'volatile Control'  and 'volatile Control'.   Apparently there are assignment operator functions for all non-volatile types and all volatile types except for bitfields.

 

I have a theory.  The guy working in his basement that writes the operator functions that everyone uses failed to include one that handles volatile bitfields.  A corollary to this theory is that I'm the only one on earth that wants to use the assignment operator to copy volatile bitfields.  It gets lonely sometimes.smiley

 

The thing my theory can't explain is if I 'hide' the volatile bitfields inside a class, gnu99 and Microsoft don't have a problem.   The fact that gnu++11 explodes when I do this lends further proof that I'm the only one on earth that does this.

 

Below, I'm displaying the source code for main.cpp and the errors generated by my 3 compilers.

 


   struct Control   {
      unsigned char software    : 1;
      unsigned char reserved    : 7;
      };

int main(void)
{
    volatile Control control_1;
    volatile Control control_2;

    control_2 = control_1;

    while(1)
    {
    volatile int count;
    count++;
    }
}

 

gnu99 errors:

 

F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(17,15): error: no match for 'operator=' (operand types are 'volatile Control' and 'volatile Control')
       control_2 = control_1;
                 ^
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(17,15): info: candidate is:
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(4,11): info: Control& Control::operator=(const Control&)
      struct Control   {
             ^
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(4,11): info:   no known conversion for argument 1 from 'volatile Control' to 'const Control&'
  make: *** [main.o] Error 1
 Done executing task "RunCompilerTask" -- FAILED.

 

gnu++11:

 

F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(17,15): error: no match for 'operator=' (operand types are 'volatile Control' and 'volatile Control')
       control_2 = control_1;
                 ^
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(17,15): info: candidates are:
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(4,11): info: Control& Control::operator=(const Control&)
      struct Control   {
             ^
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(4,11): info:   no known conversion for argument 1 from 'volatile Control' to 'const Control&'
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(4,11): info: Control& Control::operator=(Control&&)
F:\Archiveable\AVR_butterfly\Xmega\Test\Volatile_problem\main.cpp(4,11): info:   no known conversion for argument 1 from 'volatile Control' to 'Control&&'
  make: *** [main.o] Error 1
 Done executing task "RunCompilerTask" -- FAILED.

 

Microsoft:

1>..\Volatile_problem\main.cpp(17): error C2678: binary '=' : no operator found which takes a left-hand operand of type 'volatile Control' (or there is no acceptable conversion)
1>          ..\Volatile_problem\main.cpp(7): could be 'Control &Control::operator =(const Control &)'
1>          while trying to match the argument list '(volatile Control, volatile Control)'

 

Last Edited: Tue. Apr 12, 2016 - 05:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm pretty sure the default assignment operator for class X is member function

X & operator=(const X &rhs) { ... ; return *this; }

If the lhs is volatile, you need

ReturnType operator=(const X &rhs) volatile { ... }

If both might be volatile, you need

ReturnType operator=(const volatile X &rhs) volatile { ... }

Return type can be void if you want.

The traditional return value is *this;

If the return type is volatile and you do not chain,

you can get a warning about unaccessed volatile.

If not, then you are lying about the volatile-ness of the returned reference.

struct Control   {
      unsigned char software    : 1;
      unsigned char reserved    : 7;

      // both work, but I recommend against chaining
      #if 0
      Control & operator=(const volatile Control &rhs) volatile {
          software=rhs.software;
          reserved=rhs.reserved;
          return *(Control*)this;
      }
      #else
      Control & operator=(const volatile Control &rhs) volatile {
           return *(Control*)this=rhs;
      }
      #endif
};

struct Simple {
      int mem;
} ;


int main(void)
{
    volatile Control control_v;
    Control control_nv;

    control_v=control_v;
    control_nv=control_v;
    control_nv=control_nv;  // allowed wo =
    control_v=control_nv;

    (void)(control_v=control_v);

    Simple snv;
    volatile Simple sv;

    snv=snv;
    snv=sv;
    sv=sv;
    sv=snv;

    while(1)
    {
    volatile int count;
    count++;
    }
}

[hennebry@localhost tmp]$ g++ -std=c++0x -c v.cc
v.cc: In function ‘int main()’:
v.cc:39: error: no match for ‘operator=’ in ‘snv = sv’
v.cc:19: note: candidates are: Simple& Simple::operator=(const Simple&)
v.cc:40: error: no match for ‘operator=’ in ‘sv = sv’
v.cc:19: note: candidates are: Simple& Simple::operator=(const Simple&)
v.cc:41: error: passing ‘volatile Simple’ as ‘this’ argument of ‘Simple& Simple::operator=(const Simple&)’ discards qualifiers
[hennebry@localhost tmp]$

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks skeeve.  It's good to get some info from someone who knows his stuff.  I don't really understand it very well, but I tried something and now all 3 compilers compile without errors.  The gnu compilers don't have warnings either.  The Microsoft compiler gives me a warning but it may be an erroneous warning. Anyway my Microsoft compiler is probably the one that came with my Visual C++ 2010.  I might try it on a newer Visual C++.

 

Here is what I did.

 


   struct Control   {
      unsigned char software    : 1;
      unsigned char reserved    : 7;

      Control & operator=(const volatile Control &rhs) volatile {
         return *(Control*)this=rhs;
      }

      };

int main(void)
{
    volatile Control control_1;
    volatile Control control_2;

    control_2 = control_1;

    while(1)
    {
    volatile int count;
    count++;
    }
}

 

Here is Microsoft's warning:

main.cpp(8): warning C4717: 'Control::operator=' : recursive on all control paths, function will cause runtime stack overflow

 

This is a good first step.  Is there another step?  It won't be useful if I have to put it into every bitfield struct.  I have a hundred of them and counting.

 

It might help understanding why my compilers can't compile.  Maybe they are lacking something they should have?

Last Edited: Tue. Apr 12, 2016 - 09:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I decided it might be a good idea to run the program.  I was so happy that it compiled, I forgot about running it.

 

It looks like the Microsoft warning is correct.  All the builds are stuck on the line:

 

return *(Control*)this=rhs;

Last Edited: Tue. Apr 12, 2016 - 10:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried using the part of the operator= function that you #if 0 out and it compiles with all compilers and runs fine.  smiley

Last Edited: Wed. Apr 13, 2016 - 12:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
return *(Control*)this=(Control &)rhs;

should work without recursion.
 

Note that it works by casting away volatile issues.

Lies about volatile can come back to bite one,

but they don't always.

Moderation in all things. -- ancient proverb

Last Edited: Wed. Apr 13, 2016 - 02:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

return *(Control*)this=(Control &)rhs;

should work without recursion.

It still recurses and Microsoft's compiler still gives the warning.

 

skeeve wrote:

Note that it works by casting away volatile issues.

Lies about volatile can come back to bite one,

but they don't always.

I don't understand.   Are you suggesting the compilers are buggy?  They seem buggy to me.

 

 

Last Edited: Wed. Apr 13, 2016 - 10:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

skeeve wrote:

return *(Control*)this=(Control &)rhs;

should work without recursion.

It still recurses and Microsoft's compiler still gives the warning.

I'd forgotten that a user-defined operator= usually removes the default operator= .

C++11 provides a way around that:

struct Control   {
      unsigned char software    : 1;
      unsigned char reserved    : 7;

      Control & operator=(const Control &rhs) = default ;

      Control & operator=(const volatile Control &rhs) volatile {
           Control *that=const_cast<Control *>(this);
           const Control &rhs2(const_cast<const Control&>(rhs));
           return *that=rhs2;
      }
};

int main(void)
{
    volatile Control control_v;
    Control control_nv;

    control_v=control_v;
    control_nv=control_v;
    control_nv=control_nv;
    control_v=control_nv;

    while(1)
    {
    volatile int count;
    count++;
    }
}

Quote:
skeeve wrote:

Note that it works by casting away volatile issues.

Lies about volatile can come back to bite one,

but they don't always.

I don't understand.   Are you suggesting the compilers are buggy?  They seem buggy to me.
No.

I'm suggesting that lying to the compiler can come back to bite you.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

I don't understand.   Are you suggesting the compilers are buggy?  They seem buggy to me.

No.

I'm suggesting that lying to the compiler can come back to bite you.

I still don't understand.  Who's lying?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried building your main() with the control_v and control_nv.  It builds with C++11 but it doesn't seem to do anything except the count++ loop.  It pushes a few registers on the stack, does a couple of IN from I/O locations and then goes to the count loop at the bottom.  It doesn't seem to run your operator= functions at all. 

 

I don't know much about assembler and you may not either, but here it is anyway.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I get the same result on a PC.

We might be dealing with a compiler bug here.

I tried making the Control variables global and defined in another file,

but the same result.

 

This works:

struct Control   {
      unsigned char software    : 1;
      unsigned char reserved    : 7;

      Control & operator=(const volatile Control &rhs) volatile {
           software=rhs.software;
           reserved=rhs.reserved;
           return const_cast<Control &>(*this);
      }
};

 

Moderation in all things. -- ancient proverb

Last Edited: Thu. Apr 14, 2016 - 06:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With your latest assignment function, I now get correct results in 3 of 4 cases.  I have Visual Studio 2015 on another computer.  I'm now testing with it as well as gnu++11.

 

With Microsoft's 2015, everything works whether the bitfield struct is in a class or not.

 

With gnu++11, both cases build and run but when the struct is in the class, the assignment statement is missing.  main() goes directly to the loop at the bottom.  When the struct is not in a class, it builds and runs okay.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In case anyone cares, and that seems highly unlikely, I got a little info, unsatisfactory as it is, from Microsoft.

 

First of all, they say the volatile problem isn't just bit fields, it is with all structs. 

 

Second, they seem okay with the status quo.

 

Let me mention again that qnu's compiler and Microsoft's compiler have the same problem.  Apparently they both get their compilers from Compilers-Я-Us.

 

I got this from one of their posts:

If a structure does not have a length that can be copied on the current architecture by using one instruction, 
volatile may be completely lost on that structure.

Here's Microsoft's official statement on volatiles which contains that sentence, and also mentions that C++11 ISO standard changed the definition of volatile.  I guess that explains why copying a class that contains volatile structs with the assignment operator used to work:

https://msdn.microsoft.com/en-us/library/12a04hfd.aspx?f=255&MSPPError=-2147217396

 

Notice the above URL ends with Error=.....   I think the whole page is an error.wink

 

Fortunately the fix is pretty easy.  I just use some copy_memory function that just copies bytes from one place to another.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your manual copying has a different volatile behavour.  In the original copy it's undefined how they should be sequenced.
 

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also there is the following constraint on copy elision:

 

In those cases where copy-elision is not guaranteed, if it takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
 

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are no doubt right that there could be some inconsistencies when I do the copy.  However in this case that is virtually irrelevant.  I copy the registers of the various peripherals into  messages and send them to a PC for display.  Such concerns are almost nonexistent.  I can display all the AVR's registers on the PC and can change them too.  This can be very helpful in writing and debugging and testing AVR software.

 

Here is an example of the registers of 4 peripherals from one of my Xmegas.  For instance, I can see that I have all 3 RC oscillators running.  I don't use the 2 MHz osc.  I could disable it with a couple of mouse clicks.  I can see that the system clock is 48 MHz.  That is necessary for USB.  I can see the PER/CPU clock is 24 MHz.  I can change that to 12 MHz with a couple of mouse clicks. 

 

 

 

Yep, just 2 mouse clicks to disable the 2 MHz osc.  Programming a computer was never so easy.  smiley

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No opinions on this?

 

If a structure does not have a length that can be copied on the current architecture by using one instruction,
volatile may be completely lost on that structure.

 

My structs are one byte in size.  Are they saying that is too big?