 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Andrei Alexandrescu (See Guest
|
Posted: Sat Jul 17, 2004 12:03 pm Post subject: overloading operator new |
|
|
While reading items 22 and 23 in Herb Sutter's Exceptional C++ (about new
overloading) I came to an interesting question.
For a derived class, is it possible to call the base version of new if the
base happened to overload it, or else call the global version?
IOW: I design class D derived from B. I add some overload of new:
class D : public B {
public:
void* operator new(size_t, Gizmo&);
};
Now according to Herb's guideline, D's author should provide all of other
legit overloads. But the thing is, D's writer does not to modify D whenever
B's implementer chose to define (or not) their own overloads. If B overloads
new for sure, cool:
class D : public B {
public:
void* operator new(size_t, Gizmo&);
using B::operator new;
};
If B did not overload new, cool again:
class D : public B {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return ::operator new(s);
}
... etc. etc. ...
};
But how can D's author unify the two such that D does the right thing
whether or not B overloaded new?
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Vladimir Prus Guest
|
Posted: Mon Jul 19, 2004 10:32 am Post subject: Re: overloading operator new |
|
|
Hi Andrei,
| Quote: | class D : public B {
public:
void* operator new(size_t, Gizmo&);
using B::operator new;
};
If B did not overload new, cool again:
class D : public B {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return ::operator new(s);
}
|
....
| Quote: | But how can D's author unify the two such that D does the right thing
whether or not B overloaded new?
|
I think a possible approach was suggested by Peter Dimov on the Boost
mailing list. The original post is
http://article.gmane.org/gmane.comp.lib.boost.devel/92076
and it can be applied to this case like this:
template<class T> struct helper: public T
{
static void * alloc(size_t size)
{
return operator new(size);
}
};
class D : public B {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper<B>::alloc(s);
}
Complete example at http://zigzag.cs.msu.su/~ghost/new.cpp appears to do
what you want.
- Volodya
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Herb Sutter Guest
|
Posted: Mon Jul 19, 2004 8:53 pm Post subject: Re: overloading operator new |
|
|
On 19 Jul 2004 06:32:48 -0400, Vladimir Prus <ghost (AT) cs (DOT) msu.su> wrote:
| Quote: | template<class T> struct helper: public T
{
static void * alloc(size_t size)
{
return operator new(size);
}
};
class D : public B {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper<B>::alloc(s);
}
Complete example at http://zigzag.cs.msu.su/~ghost/new.cpp appears to do
what you want.
|
Cool. What if there are multiple base classes -- will this work?
class D : public B1, public B2, public B3 {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper< GenScatterHierarchy
};
Andrei?
Also, note that there are three forms of op new that you want, not just
this one. So what you really need is (sketching):
template<class T> struct helper : public T {
void* alloc(std::size_t s) {
return operator new(s);
}
void* alloc(std::size_t s, std::nothrow_t nt) throw() {
return operator new(s, nt);
}
void* alloc(std::size_t s, void* p) {
return operator new(s, p);
}
};
And then in each class you have to write:
class D : public B1, public B2, public B3 {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper< GenScatterHierarchy
void* operator new(std::size_t s, std::nothrow_t nt) throw() {
return helper< GenScatterHierarchy
void* operator new(std::size_t s, void* p) {
return helper< GenScatterHierarchy
};
Could you simplify this further by the adjustment:
template<class T> struct helper_1 : public GenScatterHierarchy<T> {
void* alloc(std::size_t s) {
return operator new(s);
}
void* alloc(std::size_t s, std::nothrow_t nt) throw() {
return operator new(s, nt);
}
void* alloc(std::size_t s, void* p) {
return operator new(s, p);
}
};
// (I think this will give you errors if more than one of the
// bases defined op new, but that's probably a feature.)
template<class T> struct helper_2 : public helper_1<T> {
void* operator new(size_t s) {
return helper_1<T>::alloc(s);
}
void* operator new(std::size_t s, std::nothrow_t nt) throw() {
return helper_1<T>::alloc(s,nt);
}
void* operator new(std::size_t s, void* p) {
return helper_1<T>::alloc(s,p);
}
};
Then you can just write:
class D : helper_2<TYPELIST_3(B1,B2,B3)> {
public:
void* operator new(size_t, Gizmo&);
using helper_2<TYPELIST_3(B1,B2,B3)>::operator new;
};
That's just a sketch. Would such an approach work?
Herb
---
Herb Sutter (www.gotw.ca)
Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Visual C++ architect, Microsoft (www.gotw.ca/microsoft)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Marco Manfredini Guest
|
Posted: Mon Jul 19, 2004 8:55 pm Post subject: Re: overloading operator new |
|
|
Andrei Alexandrescu (See Website for Email) wrote:
| Quote: | But how can D's author unify the two such that D does the right thing
whether or not B overloaded new?
|
I think a solution along the SFINAE principle could help. Here is a
quick hack for a "has_operator_new<T>" predicate, which tells me if
there is an operator new in T defined:
//====================== BEGIN =========================================
// compile-time "has_operator_new" predicate. Not checked for quirks.
#include <stdlib.h>
typedef char (&no_tag)[0];
typedef char (&yes_tag)[1];
template<void *(*fn)(size_t)>
struct has_new_helper
{
typedef yes_tag Result;
};
// has_new_helper returns a "yes_tag" if there is a possible
// substitution for T::operator new, which matches the signature
// in has_new_helper<...>.
template<class T>
typename has_new_helper<T::operator new>::Result
has_new_fn(T*);
// default case
// (with an undesireable conversion to avoid ambigous conversion)
no_tag
has_new_fn(void*);
template<class T>
struct has_operator_new
{
enum
{
value = sizeof(has_new_fn((T*)0)) == sizeof(yes_tag)
};
};
#include <iostream>
using namespace std;
class B
{
public:
static void *operator new(size_t sz);
};
class C
{
};
int main()
{
cout << has_operator_new
cout << has_operator_new
return 0;
}
//========================== END ==============================
This should print
1
0
when compiled with decent compiler. I've checked it with gcc-3.4.1 and
msvc-7.1 (gcc-3.3.4 doesn't work!). Since has_operator_new
a compile time constant, it should be quite easy now to implement the
requested functionality.
Greetings
Marco
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Andrei Alexandrescu (See Guest
|
Posted: Wed Jul 21, 2004 8:17 pm Post subject: Re: overloading operator new |
|
|
"Herb Sutter" <hsutter (AT) gotw (DOT) ca> wrote
| Quote: | On 19 Jul 2004 06:32:48 -0400, Vladimir Prus <ghost (AT) cs (DOT) msu.su> wrote:
template<class T> struct helper: public T
{
static void * alloc(size_t size)
{
return operator new(size);
}
};
class D : public B {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper<B>::alloc(s);
}
Complete example at http://zigzag.cs.msu.su/~ghost/new.cpp appears to do
what you want.
Cool. What if there are multiple base classes -- will this work?
class D : public B1, public B2, public B3 {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper< GenScatterHierarchy
::alloc(s);
}
};
|
Cool. That should work, except when more than one base defines overloads,
case in which there's an ambiguity. By controlling the typelist you can
easily select which overload should be in effect.
I like the solution above a tad better than the one using the sizeof
operator to find the presence of the overload.
| Quote: | Also, note that there are three forms of op new that you want, not just
this one.
|
You know what. It just occured to me that each class-specific operator new
that could throw should also be accompanied by a variant that takes
std::nothrow_t as its last argument. This is a nice touch I'd think.
| Quote: | So what you really need is (sketching):
template
void* alloc(std::size_t s) {
return operator new(s);
}
void* alloc(std::size_t s, std::nothrow_t nt) throw() {
return operator new(s, nt);
}
void* alloc(std::size_t s, void* p) {
return operator new(s, p);
}
};
|
Cool, just th functions would need to have "static" prepended.
| Quote: | And then in each class you have to write:
class D : public B1, public B2, public B3 {
public:
void* operator new(size_t, Gizmo&);
void* operator new(size_t s) {
return helper< GenScatterHierarchy
::alloc(s);
}
void* operator new(std::size_t s, std::nothrow_t nt) throw() {
return helper< GenScatterHierarchy
::alloc(s,nt);
}
void* operator new(std::size_t s, void* p) {
return helper< GenScatterHierarchy
::alloc(s,p);
}
};
|
A typedef for helper<...> would help so you can later solve ambiguities.
| Quote: | Could you simplify this further by the adjustment:
template<class T> struct helper_1 : public GenScatterHierarchy<T> {
void* alloc(std::size_t s) {
return operator new(s);
}
void* alloc(std::size_t s, std::nothrow_t nt) throw() {
return operator new(s, nt);
}
void* alloc(std::size_t s, void* p) {
return operator new(s, p);
}
};
// (I think this will give you errors if more than one of the
// bases defined op new, but that's probably a feature.)
template<class T> struct helper_2 : public helper_1<T> {
void* operator new(size_t s) {
return helper_1<T>::alloc(s);
}
void* operator new(std::size_t s, std::nothrow_t nt) throw() {
return helper_1<T>::alloc(s,nt);
}
void* operator new(std::size_t s, void* p) {
return helper_1<T>::alloc(s,p);
}
};
Then you can just write:
class D : helper_2<TYPELIST_3(B1,B2,B3)> {
public:
void* operator new(size_t, Gizmo&);
using helper_2<TYPELIST_3(B1,B2,B3)>::operator new;
};
That's just a sketch. Would such an approach work?
|
Aha, so the idea is to define helper_2 as a class that *definitely*
overloads operator new, which reduces the problem to one of choosing the
appropriate using overloads.
FWIW, the inheritance above might not be of optimal size. Compilers don't
apply EBO very aggressively to multiple inheritance.
I am increasingly thinking that a "alloc_helper" class (such as the ones
sketched above) should be in the standard to allow proper overloading of new
for classes.
Andrei
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Cristian Adam Guest
|
Posted: Thu Jul 22, 2004 10:49 pm Post subject: Re: overloading operator new |
|
|
| Quote: | when compiled with decent compiler. I've checked it with gcc-3.4.1 and
msvc-7.1 (gcc-3.3.4 doesn't work!). Since has_operator_new<T>::value is
a compile time constant, it should be quite easy now to implement the
requested functionality.
|
Are you sure it's working on msvc-7.1? I've tested the program and it chokes
at line typedef char (&no_tag)[0] with these errors:
error C2466: cannot allocate an array of constant size 0
error C2265: '<Unknown>' : reference to a zero-sized array is illegal
Cheers,
Cristi.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Marco Manfredini Guest
|
Posted: Fri Jul 23, 2004 2:26 pm Post subject: Re: overloading operator new |
|
|
Cristian Adam wrote:
| Quote: | Are you sure it's working on msvc-7.1? I've tested the program and it
chokes at line typedef char (&no_tag)[0] with these errors:
error C2466: cannot allocate an array of constant size 0
error C2265: '<Unknown>' : reference to a zero-sized array is illegal
|
Ooops. I've checked it while I was writing this and forgot to sync back
the gcc-ism that slipped in:
typedef char (&no_tag)[1];
typedef char (&yes_tag)[2];
Cheers ;-)
Marco
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|