

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

I'm writing software for an embedded system.

We are using pointers to access registers of an FPGA device.
Some of the registers are read-only, while others are write-only.

The write-only registers will produce undefined values when read.

I want to define a pointer type that will allow the compiler to detect when reading values from a write-only registers (a.k.a. dereferencing).

Can a write-only pointer be created using only C language syntax?
(We are developing first prototype using C, but moving to C++ on 2nd generation.)

How can an efficient write-only pointer be created in C++? (Remember, this is not tracking items in dynamic memory, but accessing hardware addresses.)

This code is used on an embedded system where safety and quality are highest concerns.

share|improve this question
I seriously doubt that you can do it in C. In C++, on the other hand, you should be able to do it relatively easily. – dasblinkenlight Apr 17 '13 at 0:27
@Alexey Not so. Have a look at Jerry’s answer. – Konrad Rudolph Apr 17 '13 at 0:41
@dasblinkenlight Well in C you could hide them behind an opaque pointer with well-defined read / write functions. – Konrad Rudolph Apr 17 '13 at 0:42
@KonradRudolph As I said, you have to write that and stick to using it. That's discipline. – Alexey Frunze Apr 17 '13 at 0:44
@Alexey I fail to see your point. It’s as much disciplin as using int instead of std::string: none, really. Once you’ve declared your variables of the appropriate type the compiler makes sure you don’t use them wrong. – Konrad Rudolph Apr 17 '13 at 1:17
up vote 58 down vote accepted

I'd probably write a tiny wrapper class for each:

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

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

    // chaining not allowed since it's write only.
    void operator=(T const &t) volatile { *addr = t; } 

At least assuming your system has a reasonable compiler, I'd expect both of these to be optimized so the generated code was indistinguishable from using a raw pointer. Usage:

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

y = x + 1;         // No problem

x = y;             // won't compile
share|improve this answer
Nice templates! How do you combine them for read-write registers? – Giacomo Tesio Apr 17 '13 at 6:38
Why did you choose to use int and not volatile T*? If you actually want an integer, there is intptr_t. Also, operator T() could be const volatile. – Jon Purdy Apr 17 '13 at 7:47
@JonPurdy: I avoided T volatile* because that would mean the user having a read/write pointer to the register -- exactly what we wanted to avoid. Embedded compilers are often, so expecting them to include intptr_t (just added in C++11) is asking a lot. If anything, I'd make it a template parameter. I agree about const volatile -- just edited; thank you. – Jerry Coffin Apr 17 '13 at 13:30
+1. Since write-only is hard to debug, having isolated the writes to a function gives you a nice spot to add logging. I'm still concerned about cavalierly using int for an address. The last embedded system I worked on had 16-bit ints but 32-bit addresses. I would make a typedef for an address and use that, so it's easy to re-purpose the code for other systems. – Adrian McCarthy Apr 17 '13 at 16:01
@AdrianMcCarthy: A typedef could work too, but as I said above, if I were to change it from int, I'd probably just use a template parameter. For testing, you can also create a class that stores (and allows retrieval of) the last value that was written, so the rest of the code can treat it like read/write memory. – Jerry Coffin Apr 17 '13 at 16:43

I've worked with a lot of hardware, and some of which has "read only" or "write only" registers (or different functions depending on whether you read or write to the register, which makes for fun when someone decides to do "reg |= 4;" instead of remembering the value it should have, set bit 2 and write the new value, like you should. Nothing like trying to debug hardware that has random bits appearing and disappearing from registers you can't read! ;) I have so far not seen any attempts of actually blocking reads from a write-only register, or writes to read-only registers.

By the way, did I say that having registers that are "write only" is a REALLY bad idea, because you can't read back to check if the software has set the register correctly, which makes debugging really hard - and people writing drivers don't like debugging hard problems that could be made really easy by two lines of VHDL or Verilog code.

If you have some control over the register layout, I would suggest that you put "readonly" registers at a 4KB-aligned address, and "writeonly" registers in another 4KB-aligned address [more than 4KB is fine]. Then you can program the memory controller of the hardware to prevent the access.

Or, let the hardware produce an interrupt if registers that aren't supposed to be read are being read, or registers that aren't supposed to be written are written. I presume the hardware does produce interrupts for other purposes?

The other suggestions made using various C++ solutions are fine, but it doesn't really stop someone who is intent on using the registers directly, so if it's really a safety concern (rather than "let's make it awkward"), then you should have hardware to protect against the misuse of the hardware.

share|improve this answer
This is a good point where it applies, but reading back values doesn't always make conceptual sense, such as a register that appends to a fifo every time you write to it. – Owen Apr 23 '13 at 7:47
@Owen: It is still useful to be able to read back "what was the last thing I wrote to this register". But yes, I agree, there are some registers where that doesn't make much sense. – Mats Petersson Apr 23 '13 at 8:21

I would use a combination of structs to rappresent the register and a pair of functions to handle them.

In a fpga_register.h you would have something like

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

with READ and WRITE in xor to express permissions.

Than in the fpga_register.c you would define a new struct

typedef struct register_t2 {
    char permissions;
    void * address;
} FPGARegisterReal;

so that you returns a pointer to it instead of a pointer to FPGARegister on fpga_init.

Then, on fpga_read and fpga_write you check the permissions and

  • if the operetion is allowed, cast back the FPGARegister from the argument to a FPGARegisterReal, execute the desired action (set or read the value) and return a success code
  • if the operation is not allowed, just return an error code

This way, no one including the header file will be able to access the FPGARegisterReal structure, and thus it will not have direct access to the register address. Obviously, one could hack it, but I'm quite sure that such intentional hacks are not your actual concerns.

share|improve this answer

In C, you can use pointers to incomplete types to prevent all dereferencing:

/* writeonly.h */
typedef struct writeonly *wo_ptr_t;

/* 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 */

Only writeonly.c, or in general any code that has a definition struct writeonly, can dereference the pointer. That code, of course, can accidentally read the value also, but at least all other code is prevented from dereferencing the pointers all together, while being able to pass those pointers around and store them in variables, arrays and structures.

writeonly.[ch] could provide a function for writing a value.

share|improve this answer

I see no elegant way of doing it in C. I do however see a way of doing it:

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

    DEREF_PTR(int*, x)
    NO_DEREF_PTR(int*, y);

    DEREFERENCE(y); // will throw an error

This has the benefit of giving you static error checking. Of course, using this method, you'll have to go out and modify all of your pointer declarations to use macros, which is probably not a whole lot of fun.

Edit: As described in the comments.

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

    READABLE_PTR(int*, x)
    NON_READABLE_PTR(int*, y);

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

    int foo = GET(x);
    int bar = GET(y); // error
share|improve this answer
This doesn’t distinguish between reads and writes. – Konrad Rudolph Apr 17 '13 at 1:21
Whoops, that's correct. I saw his mention of detecting dereferencing and sort of got ahead of myself. – Martin Svanberg Apr 17 '13 at 1:24
However, the same principle could be used to define macros for reading and setting the value behind the pointer. – Martin Svanberg Apr 17 '13 at 1:26
@MartinSvanberg Nevertheless I'd like to understand it a bit more... can you explain how it was intended to work? – Giacomo Tesio Apr 17 '13 at 1:26
If you have a pointer x defined as a pointer that can be dereferenced (DEREF_PTR(int*, x)), the preprocessor also defines a type behind the scenes called x_DEREF_PTR. When a call to DEREFERENCE is made, it instantiates a variable of this type in a separate scope. For pointers defined with NO_DEREF_PTR, this type doesn't exist, and so an error is thrown. – Martin Svanberg Apr 17 '13 at 1:29

Your Answer


