在C++中取消引用一个指向`volatile`结构体的指针。

huangapple go评论80阅读模式
英文:

Dereference a pointer to volatile structure in C++

问题

我明白了,你只需要翻译代码部分。以下是翻译好的部分:

我有一个指向一些易失性内存的指针,我试图对其进行解引用并复制到一个未经限定的该结构的副本中(反之亦然)。内存的格式由第三方结构定义指定。在C中,我可以解引用内存,一切正常。然而,在C++中,它抱怨找不到合适的赋值运算符(clang的错误比gcc更清晰:候选函数(隐式复制赋值运算符)不可行:'this'参数的类型为'volatile vec',但方法未标记为volatile)。对于简单类型(例如整数),这种方法非常有效,但对于结构来说则失败了。

在这种特定情况下,指向的值在对象读取期间不能改变,但我必须防止它们在读取到读取时被缓存。

我找到了[我可以将易失性T隐式转换为T吗][1],它为我提供了许多要尝试的选项,但是我无法找到可以让这个实际起作用的方法。我怀疑所有这些解决方案都将由于[为什么不能使用非成员函数来重载赋值运算符?][2] 而失败,这将导致我由第三方定义的结构无法找到合适的,因为结构定义是第三方的,但这只是理论上的,因为我甚至无法使手动指定的成员函数完全起作用(也许我可以创建一个带有显式内容的包装结构?)。

下面的示例在C中编译并正常工作,但在C++中失败。不幸的是,完整问题中还有其他方面迫使使用C++。让这个工作的秘密是什么?

```c
#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));
}

尝试将魔术隐藏在extern C中(我翻译的一条评论建议)似乎并没有奏效——仍然抱怨丢弃了限定符和模糊的重载。在使用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);

<details>
<summary>英文:</summary>

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&#39;t find a suitable assignment operator (clang&#39;s error is much clearer than gcc here: candidate function (the implicit copy assignment operator) not viable: &#39;this&#39; argument has type &#39;volatile vec&#39;, 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?][1] 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?][2] 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&#39;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 &lt;stdio.h&gt;
        #include &lt;stdint.h&gt;
        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 = &amp;vvector;
          *pvector = first;
          cvector = *pvector;
          printf(&quot;%x %d %d %d\n&quot;, cvector.addr, cvector.len, cvector.id, sizeof(cvector));
        }
  
Trying to hide the magic in extern C (my translation of a comment suggestion) didn&#39;t seem to work--still complained about discarded qualifiers and ambiguous overloads.  Still works fine when using C.

        extern &quot;C&quot; {
          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, &amp;first);
      uvcopy(&amp;cvector, pvector);

 

  [1]: https://stackoverflow.com/questions/17220498/can-i-add-an-implicit-conversion-from-a-volatile-t-to-a-t
  [2]: https://stackoverflow.com/questions/3933637/why-cannot-a-non-member-function-be-used-for-overloading-the-assignment-operator?noredirect=1&amp;lq=1


</details>


# 答案1
**得分**: 2

`volatile` 需要原子操作。默认的 `operator=` 无法遵守这一点。

如果数据在复制时没有发生变化,当然可以使用 `memcpy` 或逐个复制字段。但 `volatile` 关键字表示数据可能在复制时发生变化。

如果你在64位系统上工作,可以使用 `uint64_t`。但在你的例子中,这只会起作用,因为 `sizeof(vec) == 8`。

例如:


    #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));
    }


<details>
<summary>英文:</summary>

`volatile` needs atomic operation. Default `operator=` can&#39;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 &lt;stdint.h&gt;
    #include &lt;stdio.h&gt;
    
    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&lt;volatile uint64_t *&gt;(&amp;vvector);
    
        *pvector = *reinterpret_cast&lt;uint64_t *&gt;(&amp;first);
    
        *reinterpret_cast&lt;uint64_t *&gt;(&amp;cvector) = *pvector;
    
        printf(&quot;%x %d %d %zu\n&quot;, cvector.addr, cvector.len, cvector.id,
               sizeof(cvector));
    }



</details>



# 答案2
**得分**: 0

需要定义适当的赋值运算符重载:

```cpp
vec& operator=(volatile vec&);
vec& operator=(vec&) volatile;

(在godbolt上的工作示例:https://godbolt.org/z/jnexj4fMh)

另请参阅https://stackoverflow.com/questions/4644296/cant-assign-an-object-to-a-volatile-object

如果你希望相同的代码在C和C++中都能工作,最简单的方法是复制struct vec的各个POD成员,而不是整个结构体。

英文:

You need to define the appropriate assignment operator overloads:

vec&amp; operator=(volatile vec&amp;);
vec&amp; operator=(vec&amp;) volatile;

(Working example in godbolt: https://godbolt.org/z/jnexj4fMh)

See also https://stackoverflow.com/questions/4644296/cant-assign-an-object-to-a-volatile-object

If you want the same code to work in C and C++, the easiest way is to copy the individual POD members of struct vec instead of the whole struct.

huangapple
  • 本文由 发表于 2023年6月6日 08:10:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76410683.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定