Re: C++ user-defined 'new' (expert question)
Joseph M. Newcomer wrote:
Here's a question for the C++ experts.
Consider the following situation, reduced here to its simplest form (the real form is far
more complex, and proprietary).
I have a collection of C structures for which there are a number of functions already
defined and for which I need to write a set of C++ wrappers. In particular, there are
already functions that create objects of the C type. These functions and their behavior
are all defined by a set of C functions. So be patient with the examples, because this is
a truly-cut-down example that is fully sanitized. For example, the structure I am dealing
with is more complex than the trivial "point"-style structure shown here, the members are
not simple int values, and so on. The setter/getter functions are much more complex than
the trivial ones shown here. The 'free' function, for example, has to free a number of
internal structures. The original C code was written by a C++ programmer who was forced
to write in C, so philosophically tried to emulate C++ style.
This was done as a C++ console app for purposes of illustration
#include "stdafx.h"
typedef struct {
int x;
int y;
} MYPOINT;
MYPOINT * MYPOINT_new(int xv, int yv)
{
MYPOINT * pt = (MYPOINT *)malloc(sizeof(MYPOINT));
pt->x = xv;
pt->y = yv;
return pt;
}
void MYPOINT_free(MYPOINT * p) { free(p); }
void MYPOINT_setx(MYPOINT * pt, int x) { pt->x = x; }
void MYPOINT_sety(MYPOINT * pt, int y) { pt->y = y; }
int MYPOINT_getx(MYPOINT * pt) { return pt->x; }
int MYPOINT_gety(MYPOINT * pt) { return pt->y; }
typedef struct {
MYPOINT pt;
} MYTYPE;
MYTYPE * MYTYPE_new(MYPOINT pt)
{
MYTYPE * mt = (MYTYPE*)malloc(sizeof(MYTYPE));
MYPOINT_setx(&mt->pt, 0);
MYPOINT_sety(&mt->pt, 0);
}
void MYTYPE_setpoint(MYTYPE * obj, const MYPOINT p) { obj->pt = p; }
// Now, my problem is to move this mess back into C++ so MFC programmers
// who are using it can have something convenient.
// So I wrote a set of C++ wrappers. This has taken me into obscure
// corners of C++ where I have not worked before (such as writing
// my own very complex new/delete operators), and that's where
// the problems arise.
// Consider the following wrapper examples:
class CMyPoint : public MYPOINT {
public:
void SetX(int x) { MYPOINT_setx(this, x); }
void SetY(int y) { MYPOINT_sety(this, y); }
int GetX() const { return MYPOINT_getx(this); }
int GetY() const { return MYPOINT_gety(this); }
};
class CMyType : public MYTYPE {
public:
void SetPoint(const CMyPoint pt) { MYTYPE_setpoint(this, pt); }
void * operator new(size_t) { MYPOINT pt;
MYPOINT_setx(&pt, 0);
MYPOINT_sety(&pt, 0);
return MYTYPE_new(pt);
}
void * operator new(size_t, char *, int) { MYPOINT pt;
MYPOINT_setx(&pt, 0);
MYPOINT_sety(&pt, 0);
return MYTYPE_new(pt);
}
// Note that this second form is required because of the presence
// of DEBUG_NEW
void * operator new(size_t, MYPOINT * pt)
{ return MYTYPE_new(*pt);}
void * operator new(size_t, char *, int, MYPOINT * pt)
{ return MYTYPE_new(*pt);}
// Now, because the original C "constructors" take arguments,
// I had to add the above two methods, or so I thought.
// Stroustrup is remarkably over-succinct about parameterized
// 'new' operators. Note that because the C functions
// require up to six parameters and do complex initialization,
// it is completely inappropriate to attempt to replicate
// the code in the bodies of the C '_new' functions
// so I could not replicate the trivial code that is
// shown in these examples. Some of these C functions
// are up to 50 lines long
// Note also that most of these objects must be pointer types
// to objects on the heap, so nominally I should *not*
// be able to write
// CMyType t;
};
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
int _tmain(int argc, _TCHAR* argv[])
{
CMyType * t = new CMyType;
CMyPoint pt;
pt.x = 1;
pt.y = 2;
t->SetPoint(pt);
CMyType * mt = new CMyType(&pt);
// Note that this does not use the 'new' operator
// I have defined, but issues the error message
// error C2664: 'CMyType::CMyType(const CMyType &)' : cannot convert parameter 1 from
'CMyPoint *' to 'const CMyType &'
// which suggests it is trying to use the implicit placement constructor form
// So how DO I create user-defined 'new' operators with several parameters?
delete mt;
delete t;
return 0;
}
// Another problem is that I would like the C++ classes to actually
// be 'protected', e.g.,
// class CMyPoint : protected MYPOINT
//
// typedef struct {
// ... stuff
// } random_class;
//
// void random_class_do_something(MYPOINT * pt) { ...stuff... }
//
// But this leads to a problem where I have constructs such as
// class CMyRandomClass : protected random_class
// {
// public:
// void DoSomething(CMyPoint * x) { random_class_do_something(x); }
// }
//
// where I get something like
//
// error C2243: 'type cast' : conversion from 'CMyPoint *' to 'MYPOINT *' exists, but is
inaccessible
//
// I need an error-free way to convert a CMyPoint to the C structure from which it is
// derived while still retaining the ability to hide all the members of the C struct I'm
// using as the superclass, and make them inaccessible
//
// The information about C2243 I've been able to find simply says
// "make the derivation public"
// which is not at all what I want to do. But no other workaround seems obvious; I've
// exhaused my experimentation.
//
// Note that the C++ class are intended as pure "interfaces"; they have no data, and if
// there were a way to make them "final" classes, that's what I'd want to do.
thanks
joe
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joe,
I know, the placement new operator has a confusing syntax, but the reason the correct way to do it is elusive is that it wuld
otherwise be ambiguous as to which new operator was being called. Here's the correct way to call the overloaded new operator you want:
CMyType * mt = new (&pt) CMyType;
In other words, your parameter is for the new operator, not the CMyType constructor. That's the reason for all the confusion.
Note, also, that you need to create matching delete operators for the same parameter lists, or you could end up with leaks if an
exception is thrown (warning C4291).
Scot