C++ 只写指针类型

C++ 只写指针类型,c++,c,pointers,readonly,const-correctness,C++,C,Pointers,Readonly,Const Correctness,我正在为嵌入式系统编写软件 我们使用指针访问FPGA设备的寄存器。 一些寄存器是只读的,而另一些寄存器是只读的 只读寄存器在读取时将产生未定义的值 我想定义一种指针类型,允许编译器在从只写寄存器读取值时进行检测(也称为解引用) 只能使用C语言语法创建只写指针吗? (我们正在使用C开发第一个原型,但是在第二代移动到C++) 如何在C++中创建一个高效的只写指针? (请记住,这不是跟踪动态内存中的项目,而是访问硬件地址。) 该代码用于安全性和质量是最高关注点的嵌入式系统 我可能会为每种类型编写一个小

我正在为嵌入式系统编写软件

我们使用指针访问FPGA设备的寄存器。
一些寄存器是只读的,而另一些寄存器是只读的

只读寄存器在读取时将产生未定义的值

我想定义一种指针类型,允许编译器在从只写寄存器读取值时进行检测(也称为解引用)

只能使用C语言语法创建只写指针吗?
(我们正在使用C开发第一个原型,但是在第二代移动到C++) 如何在C++中创建一个高效的只写指针? (请记住,这不是跟踪动态内存中的项目,而是访问硬件地址。)


该代码用于安全性和质量是最高关注点的嵌入式系统

我可能会为每种类型编写一个小包装器类:

template <class T>
class read_only {
    T volatile *addr;
public:
    read_only(int address) : addr((T *)address) {}
    operator T() volatile const { return *addr; }
};

template <class T>
class write_only { 
    T volatile *addr;
public:
    write_only(int address) : addr ((T *)address) {}

    // chaining not allowed since it's write only.
    void operator=(T const &t) volatile { *addr = t; } 
};
模板
类只读{
T*addr;
公众:
只读(int地址):地址((T*)地址){}
运算符T()volatile const{return*addr;}
};
模板
类只写{
T*addr;
公众:
只写(int地址):地址((T*)地址){}
//不允许链接,因为它是只写的。
void运算符=(T const&T)volatile{*addr=T;}
};
至少假设您的系统有一个合理的编译器,我希望这两个编译器都能得到优化,这样生成的代码就无法与使用原始指针区分开来。用法:

read_only<unsigned char> x(0x1234);
write_only<unsigned char> y(0x1235);

y = x + 1;         // No problem

x = y;             // won't compile
只读x(0x1234);
只写y(0x1235);
y=x+1;//没问题
x=y;//不会编译

我将使用结构的组合来表示寄存器,并使用一对函数来处理它们

fpga\u register.h

#define FPGA_READ = 1; 
#define FPGA_WRITE = 2;
typedef struct register_t {
    char permissions;
} FPGARegister;

FPGARegister* fpga_init(void* address, char permissions);

int fpga_write(FPGARegister* register, void* value);

int fpga_read(FPGARegister* register, void* value);
使用xor中的读写来表示权限

而不是在
fpga\u register.c
中定义一个新的结构

typedef struct register_t2 {
    char permissions;
    void * address;
} FPGARegisterReal;
因此,您将返回指向它的指针,而不是
fpga\u init
上指向
FPGARegister
的指针

然后,在
fpga\u read
fpga\u write
上检查权限和

  • 如果允许该操作,请将参数中的
    FPGARegister
    回溯到
    FPGARegisterReal
    ,执行所需操作(设置或读取值)并返回成功代码
  • 如果不允许该操作,只需返回错误代码即可

这样,包括头文件在内的任何人都不能访问
FPGARegisterReal
结构,因此它不能直接访问寄存器地址。显然,有人可以破解它,但我很确定这种故意破解并不是你真正关心的问题。

我使用过很多硬件,其中一些具有“只读”或“只写”寄存器(或不同的功能,取决于你是读还是写寄存器,当有人决定执行“reg |=4;”时,这会很有趣与其记住它应该具有的值,不如像你应该的那样设置位2并写入新值。没有什么比尝试调试从你无法读取的寄存器中出现和消失随机位的硬件更重要的了!;)到目前为止,我还没有看到任何试图实际阻止从只写寄存器读取或写入只读寄存器的尝试

顺便问一下,我是否说过,拥有“只写”的寄存器是一个非常糟糕的主意,因为你无法读回以检查软件是否正确设置了寄存器,这使得调试非常困难——而编写驱动程序的人不喜欢调试困难的问题,这些问题可以通过两行VHDL或Verilog代码轻松解决

如果您可以控制寄存器布局,我建议您将“只读”寄存器放在4KB对齐的地址,将“写”寄存器放在另一个4KB对齐的地址[超过4KB就可以了]。然后,您可以对硬件的内存控制器进行编程,以防止访问

或者,如果不应该被读取的寄存器正在被读取,或者不应该被写入的寄存器被写入,则让硬件产生一个中断。我想硬件会产生其他用途的中断吗


使用各种C++解决方案的其他建议是好的,但它并不能真正阻止那些直接使用寄存器的人,所以如果它真的是安全问题(而不是“让我们尴尬”)那么你应该有硬件来防止硬件被误用。

我看不出C语言有什么优雅的方法。不过,我确实看到了一种方法:

#define DEREF_PTR(type, ptr) type ptr; \
typedef char ptr ## _DEREF_PTR;

#define NO_DEREF_PTR(type, ptr) type ptr; \

#define DEREFERENCE(ptr) \
*ptr; \
{ptr ## _DEREF_PTR \
attempt_to_dereference_pointer_ ## ptr;}

int main(int argc, char *argv[]) {
    DEREF_PTR(int*, x)
    NO_DEREF_PTR(int*, y);

    DEREFERENCE(x);
    DEREFERENCE(y); // will throw an error
}
这样做的好处是可以进行静态错误检查。当然,使用此方法,您必须修改所有指针声明以使用宏,这可能不是一件很有趣的事情

编辑: 如评论中所述

#define READABLE_PTR(type, ptr) type ptr; \
typedef char ptr ## _READABLE_PTR;

#define NON_READABLE_PTR(type, ptr) type ptr; \

#define GET(ptr) \
*ptr; \
{ptr ## _READABLE_PTR \
attempt_to_dereference_non_readable_pointer_ ## ptr;}

#define SET(ptr, value) \
*ptr = value;


int main(int argc, char *argv[]) {
    READABLE_PTR(int*, x)
    NON_READABLE_PTR(int*, y);

    SET(x, 1);
    SET(y, 1);

    int foo = GET(x);
    int bar = GET(y); // error
}

在C中,您可以使用指向不完整类型的指针来防止所有取消引用:





只有
writeonly.c
,或者通常任何定义为
struct writeonly
的代码才能取消对指针的引用。当然,该代码也可能意外地读取该值,但至少所有其他代码都无法一起解除对指针的引用,同时能够传递这些指针并将其存储在变量、数组和结构中


writeonly。[ch]
可以提供一个写入值的函数。

Dan Saks有一个Youtube演示,我找不到,他在哪里为嵌入式设备引入了只写寄存器

幸运的是,他在这里写了一篇更容易搜索的文章:

这是为C++11更新的文章中的代码

class write_only_T{
public:
    write_only_T(){}
    write_only_T(T const& v) : m(v){}
    write_only_T(T&& v) : m(std::move(v)){}
    write_only_T& operator=(T const& v){
        m = v;
        return *this;
    }
    write_only_T& operator=(T&& v){
        m = std::move(v);
        return *this;
    }
    write_only_T(write_only_T const&) = delete;
    write_only_T(write_only_T&&) = delete;
    write_only_T& operator=(write_only_T const&) = delete;
    write_only_T& operator=(write_only_T&&) = delete;
private:
    T m;
};
我认为如果使用这种类型,就不需要特殊的指针类型,因为只写是值的一个属性,但我可以想象一种跳过值类型的合成指针类型。
您可能需要引入只写引用类型等等。

我严重怀疑您是否能做到这一点
/* writeonly.c */
#include "writeonly.h"

struct writeonly {
  int value 
};

/*...*/

   FOO_REGISTER->value = 42;
/* someother.c */
#include "writeonly.h"

/*...*/

   int x = FOO_REGISTER->value; /* error: deref'ing pointer to incomplete type */
class write_only_T{
public:
    write_only_T(){}
    write_only_T(T const& v) : m(v){}
    write_only_T(T&& v) : m(std::move(v)){}
    write_only_T& operator=(T const& v){
        m = v;
        return *this;
    }
    write_only_T& operator=(T&& v){
        m = std::move(v);
        return *this;
    }
    write_only_T(write_only_T const&) = delete;
    write_only_T(write_only_T&&) = delete;
    write_only_T& operator=(write_only_T const&) = delete;
    write_only_T& operator=(write_only_T&&) = delete;
private:
    T m;
};