Using member new to map devices

November 18, 2011

Dan_Saks-November 18, 2011

Declaring operator new as a class member can be a handy way to provide guaranteed initialization for memory-mapped objects.

Last year, I wrote a number of columns describing alternative techniques for representing and manipulating memory-mapped devices in C and C++. Due to space limitations, the initial articles left a lot of details unresolved.1,2,3 Most of the columns I've written since have focused on filling in the missing details, including a detour into constructors and automatic initialization.4,5

Classes typically use constructors to perform object initialization. Classes for memory-mapped devices should be no different. However, as I explained in August, many common declarations for memory-mapped objects don't invoke constructors implicitly.6

In my most recent column, I explained how you can use new with placement in C++ to invoke constructors explicitly.7 Explicit constructor calls can be very useful when you need to control initialization order precisely. However, implicit constructor calls should still be the norm because they provide guaranteed initialization.

This month, I'll show you how to provide guaranteed initialization for memory-mapped devices by defining operator new as a class member.

Where we were

For my examples, I've been using a class that represents a programmable timer. The class definition looks like:

typedef uint32_t volatile device_register;

class timer_type
    enum { TICKS_PER_SEC = 50000000 };
    typedef uint32_t count_type;
    timer_type() { disable(); }
    void disable() { TMOD &= ~TE; }
    void enable() { TMOD |= TE; }
    void set(count_type c) { ... }
    count_type get() const { ... }
    enum { TE = 0x01 };
    device_register TMOD;
    device_register TDATA;
    device_register TCNT;

The class has private data members that represent the timer's device registers, along with public member functions that provide a modest assortment of basic timer operations. One of those operations is a default constructor (highlighted above in red).

The Standard C++ Library provides a placement form of operator new declared in the standard header <new> as:
void *operator new(std::size_t, void *p) throw ();
Calling this function does nothing but return the value of its second argument. Programs can use this placement operator new to construct an object at a particular address.

For example, you declare a timer object as:
extern timer_type the_timer;

and use the linker to place the object in memory. Then you can apply the constructor via the placement new-expression:
new (&the_timer) timer_type;
(I explained the placement new syntax in my previous column.7) If you define the timer using a constant pointer instead:
timer_type *const the_timer
    = reinterpret_cast<void *>(0xFFFF6000);
then you can apply the constructor via the placement new-expression:
new (the_timer) timer_type;

Using placement new doesn't provide guaranteed initialization. By declaring a memory-mapped object and then initializing it using placement new, you can inadvertently do the first step without the second. Fortunately, you can get guaranteed initialization by using member operator new to combine the steps.

< Previous
Page 1 of 2
Next >

Loading comments...