Re: C++ user-defined 'new' (expert question)
Joe,
I don't see here a reason to put the default/non-default constructor into
operator new, and the destructor into 'delete'. Your 'new' won't also cover
array allocation. Just provide the constructors separately. If using malloc
inside 'new' is not a requirement, you don't need the custom new/delete.
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
news:0aat5558t0mfj5co7pa5h3qraa3k4a07ma@4ax.com...
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