Search code examples
c++castingoperator-overloadingvolatile

Dereference a pointer to volatile structure in C++


I have a pointer to some volatile memory that I am trying to dereference and copy to a unqualified copy of that structure (and vise-versa). The format of the memory is specified by a third party structure definition. In C, I can dereference the memory and all is well. However, C++ complains that it can't find a suitable assignment operator (clang's error is much clearer than gcc here: candidate function (the implicit copy assignment operator) not viable: 'this' argument has type 'volatile vec', but method is not marked volatile). This method works great for simple types (e.g. ints) but fails for structures.

In this particular case, the values being pointed to cannot change during the read of the object, but I must prevent them from being cached from read to read.

I found Can I add an implicit conversion from a volatile T to a T? which gave me a bunch of options to try, however I was unable to find the magic which will let this actually work. I suspect that all of these solutions would fail due to Why cannot a non-member function be used for overloading the assignment operator? which will cause my third-party-defined structure to not have a suitable since the structure definition is third-part, but that is theoretical since I can't even get manually specified member functions to fully work (maybe I could create a wrapper structure with something explicit?).

The example below compiles and works in C, but fails in C++. Unfortunately there are other aspects of the fuller problem which force C++. What is the magic to let this work?

    #include <stdio.h>
    #include <stdint.h>
    struct __attribute__((__packed__)) vec {
      uint32_t addr;
      uint32_t len:24;
      uint8_t id;
    };
    struct vec first = { .addr=0xfeedface, .len=10, .id=5};
    volatile struct vec vvector;
    int main(int argc, char **argv, char **envp)
    {
      struct vec cvector;
      volatile struct vec *pvector = &vvector;
      *pvector = first;
      cvector = *pvector;
      printf("%x %d %d %d\n", cvector.addr, cvector.len, cvector.id, sizeof(cvector));
    }

Trying to hide the magic in extern C (my translation of a comment suggestion) didn't seem to work--still complained about discarded qualifiers and ambiguous overloads. Still works fine when using C.

    extern "C" {
      static void vucopy(volatile struct vec *lhs, struct vec *rhs) { *lhs = *rhs; }
      static void uvcopy(struct vec *lhs, volatile struct vec *rhs) { *lhs = *rhs; }
    }
    ...
  vucopy(pvector, &first);
  uvcopy(&cvector, pvector);

Solution

  • volatile needs atomic operation. Default operator= can't respect that.

    If data are not changed while copy, of course, you may use memcpy or copy field by field. But volatile keyword says that data may change while copy.

    If you work on a 64 bit system, you could use uint64_t. But in your example, it will work only because sizeof(vec) == 8.

    For example :

    #include <stdint.h>
    #include <stdio.h>
    
    struct __attribute__((__packed__)) vec {
        uint32_t addr;
        uint32_t len : 24;
        uint8_t id;
    };
    
    struct vec first = {.addr = 0xfeedface, .len = 10, .id = 5};
    volatile struct vec vvector;
    
    int main(int argc, char **argv, char **envp) {
        struct vec cvector;
        volatile uint64_t *pvector =
            reinterpret_cast<volatile uint64_t *>(&vvector);
    
        *pvector = *reinterpret_cast<uint64_t *>(&first);
    
        *reinterpret_cast<uint64_t *>(&cvector) = *pvector;
    
        printf("%x %d %d %zu\n", cvector.addr, cvector.len, cvector.id,
               sizeof(cvector));
    }