Boost logo

Boost Users :

From: Daniel Lidström (daniel.lidstrom_at_[hidden])
Date: 2008-03-28 05:40:05


Hi there!

(I recently posted a similar message to microsoft.public.vc.language,
but the response was so bad I thought I would post here instead.)

I have created a utility class that I think is useful, called nondynamic.
The purpose is sort of like boost::noncopyable, but I disallow any dynamic/
pointer usages. For example, & and -> are disabled. Operator new/delete as
well.
Let me show nondynamic:

// all members are private
class nondynamic
{
   // disallow operator new
   static void* operator new(std::size_t);
   static void* operator new(std::size_t, const std::nothrow_t&) throw();
   static void* operator new[](std::size_t);
   static void* operator new[](std::size_t, const std::nothrow_t&) throw();

   // disallow operator delete
   static void operator delete(void*){}
   static void operator delete(void*, const std::nothrow_t&) throw(){}
   static void operator delete[](void*){}
   static void operator delete[](void*, const std::nothrow_t&) throw(){}

   // disallow address-of
   const nondynamic* operator&() const;
   /* */ nondynamic* operator&() /* */;

   // disallow ->
   const nondynamic* operator->() const;
   /* */ nondynamic* operator->() /* */;
};

Using this class is as simple as using boost::noncopyable:

struct DataIO : private nondynamic
{ ... };

Now you are only able to use DataIO as a value type. All other usages are
disabled. I think this promotes safe programming, in that pointer usage
is minimized (it seems with pointers, new/delete/null is never far away).

Let me illustrate how this class can be used most effectively. I thought
it would be nice to use nondynamic when you have a class hierarchy, and
a factory method.

Here's the base class:

struct IDataIO
{
   virtual void Write(const std::string& str) = 0;
   virtual void Read(char* buffer, std::size_t size) = 0;
};
typedef boost::shared_ptr<IDataIO> IDataIOPtr;

A sample subclass:

struct SerialDataIO : IDataIO
{
   void Write(const std::string&) { std::cout << __FUNCTION__ << std::endl;
}
   void Read(char*, std::size_t) { std::cout << __FUNCTION__ << std::endl; }
};

Here's the wrapper class that inherits from nondynamic:

struct DataIO : IDataIO, private nondynamic
{
   DataIO(IDataIOPtr dataIO) : mDataIO(dataIO) {}

   void Write(const std::string& str) { mDataIO->Write(str); }
   void Read(char* buffer, std::size_t size) { mDataIO->Read(buffer, size);
}

private:

   IDataIOPtr mDataIO;
};

And finally, the factory:
DataIO CreateDataIO(int i)
{
   if( i==0 )
      return IDataIOPtr(new SerialDataIO);
   else
      throw std::runtime_error("Could not create DataIO");
}

Now the clients of the class hierarchy uses the wrapper, and are completely
isolated from pointers:

   DataIO dataIO = CreateDataIO(0);
   dataIO.Write("Hello");
   char buffer[1024];
   dataIO.Read(buffer, sizeof(buffer));

It should be impossible to misuse DataIO.
What do you think about nondynamic? Is it implemented correctly?
Named appropriately? Useful at all? Any comments gladly accepted :-)

Thanks in advance!

-- 
Daniel

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net