|
what is the license for it?
I can't see it in the "About Article" section.
|
|
|
|
|
Hi,
I just compiled the sample and it works nice!
However I have quite a problem - maybe I just dont get it right..
Here is my problem:
delegate_impl stores in its member
Class* obj a copy of the the pointer, which is taken from the object who's method should be called later. What if that object is destroyed before the delegate? Sure the delegate will not know about this, resulting in an access violation.
E.g.
<br />
struct A {<br />
virtual void func(int x) {<br />
std::cout << "A::func: x = " << x << std::endl;<br />
}<br />
};<br />
int main(int argc, char* argv[])<br />
{<br />
A* pa = new A();<br />
delegate<int, void> fnTest(pa,&A::func);<br />
delete pa;<br />
fnTest(0);
return 0;<br />
}<br />
To resolve this one could use smart pointers with reference counting instead of ordinary pointers to object, but that would limit the use to dynamic objects.
Another approach is to presuppose a mechanism in the Class which removes the Class* pointer from all delegates if Class is destroyed. I.e. it renders all delegates useless. In dirty code:
<br />
struct A <br />
{<br />
List<delegate*> ListOfDelegates;<br />
<br />
~A()<br />
{<br />
for every (delegate* pDlg) in ListOfDelegates<br />
{<br />
pDlg->obj = NULL;<br />
}<br />
}<br />
virtual void func(int x);<br />
};<br />
<br />
delegate::delegate(Class* obj, Result (Class::* method)(T1))<br />
{<br />
...<br />
obj->ListOfDelegates.Add(this);<br />
}<br />
<br />
delegate::~delegate()<br />
{<br />
...<br />
obj->ListOfDelegates.Remove(this);<br />
}<br />
<br />
Result delegate::operator()(T1 v1) <br />
{<br />
if (obj)<br />
{<br />
return (obj->*method)(v1);<br />
}<br />
}<br />
}<br />
CONCLUSION: In my opinion one CAN NOT create a C++ equivalent of C# delegates. This is by design since C++ has no garbage collector. In C++ one has to worry all the time about the validity of pointers and references*. Hiding the pointers in a C++ delegate class does not lift the programmer from that burden, it just adds a nicer syntax.
Semmel
* C#, awkward and evil (Bytecode!) as it is, stretches out its tentacles to us C++ programmers holding the alluring garbage collector.
|
|
|
|
|
'CAN NOT' is taking things a bit far.
Its possible to write a smart pointer in c++ that tracks the deletion of an object and dereferences to zero once the object it points to has been destroyed (but it does have overhead).
You can also write a garbage collector in c++ if you need one, but in c# you are forced to use the garbage collector even when you don't need or want to. I prefer deterministic destruction, thankyou.
This example of the delegate isn't really like a c# delegate though, because it doesn't support multicasting.
I generally prefer the fast delegate (available on code project), because it uses less overhead at call time and works more like closure pointers from borlands c++ builder.
|
|
|
|
|
Looks like the reference count never calls "delete this" in the delegate_base class.
For a quick and dirty memory leak test, stick this in at the top of the file (from another article somewhere):
#include <crtdbg.h><br />
class CoreLibMemCheck<br />
{<br />
_CrtMemState m_checkpoint;<br />
<br />
public:<br />
<br />
CoreLibMemCheck()<br />
{<br />
_CrtMemCheckpoint(&m_checkpoint);<br />
};<br />
<br />
~CoreLibMemCheck()<br />
{<br />
_CrtMemState checkpoint;<br />
_CrtMemCheckpoint(&checkpoint);<br />
<br />
_CrtMemState diff;<br />
_CrtMemDifference(&diff, &m_checkpoint, &checkpoint);<br />
<br />
_CrtMemDumpStatistics(&diff);<br />
_CrtMemDumpAllObjectsSince(&diff);<br />
};<br />
};<br />
<br />
CoreLibMemCheck MemCheck;
Should:
void release() {<br />
if(--ref_count < 0)<br />
delete this;<br />
}<br />
be:
void release() {<br />
if(--ref_count == 0)<br />
delete this;<br />
}
?
Anywho, i'm not taking shots at ya... this is exactly what i've been trying to find for quite some time! Most solutions require the overhead of Boost, etc., and it's nice to see something so simply and elegantly done. Kudos!
|
|
|
|
|
Thanks! But.. Hmm, I thought I had fixed that one along time ago. How embarrising!
--
Look straight into the light!
|
|
|
|
|
error C2562: '()' : 'void' function returning a value
===========================
Result operator()(T1 v1)
{
return (*pDelegateImpl)(v1);
}
===========================
I've overloaded the 'call' operator before and didn't get that error.
William
Fortes in fide et opere!
|
|
|
|
|
It's the VC6 compiler that can't handle void properly. Contrary to common belief, the following is legal C++:
void func() {
}
void func2() {
return func();
} Result in your case must be void . A workaround is to define Result to be int or whatever and just return a dummy value. Not pretty, but it works.
--
Weiter, weiter, ins verderben.
Wir müssen leben bis wir sterben.
Are you bright too?[^]
|
|
|
|
|
I've been wandering for a long time on how to implement this. It's a greate trick for creating event-handlers.
Good job, Jörgen.
/*----------------------------------*
Rui A. Rebelo
rui_a_rebelo@despammed.com
Edmonton, Alberta, Canada
*----------------------------------*/
|
|
|
|
|
Thank you for your kind words!
--
Must I be the meat in an imbecill sandwich?
|
|
|
|
|
Here is my own version of your both delegate classes.
I wanted to be able to declare both static and non-static class methods as regular C# delegates do.
I also needed comparison operators and copy constructor, so i just added them.
Is there anyone here interested for adding multi-param to this already tricky stuff ?
Any comments guys ?
<br />
<br />
template <typename RESULT_TYPE, typename PARAM_TYPE><br />
class delegate_base <br />
{<br />
public:<br />
typedef delegate_base<RESULT_TYPE, PARAM_TYPE> SELF;<br />
typedef RESULT_TYPE (SELF::* pfFuncObject)(PARAM_TYPE);<br />
typedef RESULT_TYPE (*pfFuncStatic)(PARAM_TYPE);<br />
<br />
typedef union {<br />
pfFuncObject pfObject;<br />
pfFuncStatic pfStatic;<br />
} uFunc;<br />
<br />
delegate_base (void) {<br />
m_obj = NULL;<br />
m_func.pfObject = NULL;<br />
m_func.pfStatic = NULL;<br />
}<br />
<br />
virtual bool Equals (delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, delegate_base<RESULT_TYPE, PARAM_TYPE> &other) = 0;<br />
<br />
virtual RESULT_TYPE Invoke(delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, PARAM_TYPE param) = 0;<br />
<br />
void * m_obj; <br />
uFunc m_func; <br />
};<br />
<br />
template< typename CLASS_TYPE, typename RESULT_TYPE, typename PARAM_TYPE > <br />
class delegate_object : public delegate_base < RESULT_TYPE, PARAM_TYPE > <br />
{ <br />
typedef RESULT_TYPE (CLASS_TYPE::* pfDelegateFunc)(PARAM_TYPE);<br />
<br />
public: <br />
bool Equals (delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, delegate_base<RESULT_TYPE, PARAM_TYPE> &other) <br />
{<br />
delegate_object<CLASS_TYPE, RESULT_TYPE, PARAM_TYPE> *other_obj = dynamic_cast<delegate_object<CLASS_TYPE, RESULT_TYPE, PARAM_TYPE> *>(&other);<br />
if (other_obj == NULL)<br />
return false;<br />
<br />
if ((dlg.m_obj == other_obj->m_obj) &&<br />
(dlg.m_func.pfObject == other_obj->m_func.pfObject))<br />
return true;<br />
<br />
return false;<br />
} <br />
<br />
RESULT_TYPE Invoke(delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, PARAM_TYPE param)<br />
{ <br />
return (static_cast<CLASS_TYPE*>(dlg.m_obj)->*((pfDelegateFunc)dlg.m_func.pfObject))(param); <br />
} <br />
};<br />
<br />
template< typename RESULT_TYPE, typename PARAM_TYPE > <br />
class delegate_static : public delegate_base < RESULT_TYPE, PARAM_TYPE > { <br />
public:<br />
<br />
bool Equals (delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, delegate_base<RESULT_TYPE, PARAM_TYPE> &other) <br />
{<br />
delegate_static<RESULT_TYPE, PARAM_TYPE> *other_static = dynamic_cast<delegate_static<RESULT_TYPE, PARAM_TYPE> *>(&other);<br />
if (other_static == NULL)<br />
return false;<br />
<br />
if (dlg.m_func.pfStatic == other_static->m_func.pfStatic)<br />
return true;<br />
<br />
return false;<br />
} <br />
<br />
RESULT_TYPE Invoke(delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, PARAM_TYPE param)<br />
{ <br />
return (dlg.m_func.pfStatic)(param);<br />
}<br />
};<br />
<br />
template <typename RESULT_TYPE, typename PARAM_TYPE><br />
class delegate : public delegate_base < RESULT_TYPE, PARAM_TYPE ><br />
{ <br />
public: <br />
<br />
delegate () {<br />
m_type = NULL;<br />
}<br />
<br />
bool isNull() { return m_type == NULL; }<br />
<br />
template <typename CLASS_TYPE, typename RESULT_TYPE, typename PARAM_TYPE> <br />
delegate(CLASS_TYPE& obj, RESULT_TYPE (CLASS_TYPE::* method)(PARAM_TYPE))<br />
{ <br />
static delegate_object<CLASS_TYPE, RESULT_TYPE, PARAM_TYPE> DLG_OBJ;<br />
<br />
m_obj = &obj;<br />
m_func.pfObject = (pfFuncObject)(method);<br />
m_type = &DLG_OBJ;<br />
<br />
} <br />
<br />
delegate(RESULT_TYPE (* method)(PARAM_TYPE))<br />
{ <br />
static delegate_static<RESULT_TYPE, PARAM_TYPE> DLG_STC;<br />
<br />
m_obj = NULL;<br />
m_func.pfStatic = (pfFuncStatic)(method);<br />
m_type = &DLG_STC;<br />
} <br />
<br />
delegate<RESULT_TYPE, PARAM_TYPE>& operator=(const delegate<RESULT_TYPE, PARAM_TYPE>& other) {<br />
m_obj = other.m_obj;<br />
m_func = other.m_func;<br />
m_type = other.m_type;<br />
<br />
return *this;<br />
} <br />
<br />
RESULT_TYPE Invoke(delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, PARAM_TYPE param) {<br />
return m_type->Invoke(dlg, param);<br />
}<br />
<br />
RESULT_TYPE operator()(PARAM_TYPE param) {<br />
return Invoke (*this, param);<br />
}<br />
<br />
bool Equals (delegate_base<RESULT_TYPE, PARAM_TYPE> &dlg, delegate_base<RESULT_TYPE, PARAM_TYPE> &other) {<br />
if (&dlg == &other)<br />
return true;<br />
<br />
if (&dlg == NULL || &other == NULL)<br />
return false;<br />
<br />
return m_type->Equals (dlg, other);<br />
} <br />
<br />
bool operator==(const delegate<RESULT_TYPE, PARAM_TYPE>& other) {<br />
return Equals (*this, const_cast<delegate<RESULT_TYPE, PARAM_TYPE> &>(other));<br />
} <br />
<br />
bool operator!=(const delegate<RESULT_TYPE, PARAM_TYPE>& other) {<br />
return !Equals (*this, const_cast<delegate<RESULT_TYPE, PARAM_TYPE> &>(other));<br />
} <br />
<br />
private:<br />
delegate_base<RESULT_TYPE, PARAM_TYPE> * m_type;<br />
};<br />
<br />
|
|
|
|
|
It's hard to follow it, since of the original code much is gone. Mainly <'s being interpreted by the browser. But from what I can tell it's a nice addition. I'll keep this in mind until the next time I get to do some work on the delegates. I'm planning on using them in a project of mine, but I have not yet begun that coding part. When I get there, I'll revise the code and add your proposed changes (and update the article of course )
boutblock wrote:
Is there anyone here interested for adding multi-param to this already tricky stuff ?
What exactly do you mean by multi-param? Variable arguments being packed into an object[] array? That doesn't really fit into C++, mainly because there is no object nor any multi-param language construct. Well, there is, but it's the good old C way of doing things ... . I don't see how you could forward such parameters without making assumptions on how the compiler generates code for function calls (evil! ).
--
Shine, enlighten me - shine
Shine, awaken me - shine
Shine for all your suffering - shine
|
|
|
|
|
About multi params as object[], of course not
I just meant to insert what Mike Junkins already did to your code ! hehe
Now you're talking about weird ideas dealing with the way compilers work, I've got one. I'd like to be able to use those delegates as one can in C#, that is, passing the 'this' pointer as the pointed object implicitly.
I tried and failed with a mix of templates and inheritage. Only thing i found as close as possible was to define a new method of my this object to encapsulate the delegate construction (poor result
|
|
|
|
|
boutblock wrote:
I'd like to be able to use those delegates as one can in C#, that is, passing the 'this' pointer as the pointed object implicitly.
For that you'll have to tweak the compiler I'd say.
--
Shine, enlighten me - shine
Shine, awaken me - shine
Shine for all your suffering - shine
|
|
|
|
|
I forgot about implementing copy constructors and assignment operator for the delegates . They should pay respect to the reference counting..
I'll fix it when I get home.
--
Iron Maiden (Harris/Gers) wrote:
The rebel of yesterday, tomorrow's fool
Who are you kidding being that cool?
|
|
|
|
|
Jörgen,
Nice article, fun to read about interesting ways to use templates.
I downloaded the code and started experimenting with ways to remove the heap allocation. Old style C casts (C++ cast operator) seems to do the trick. I've only tested it by using the example you sent (release and debug builds) however so there might be some bugs lurking...
Anyway, here's a new delegate class that consolidates all the behavior into one class. It uses a templated static method in a nested class to 'save' the behavior required to do the proper cast.
template <typename PARAM_TYPE, typename RESULT_TYPE>
class delegate2
{
typedef delegate2<PARAM_TYPE,RESULT_TYPE> SELF;
typedef RESULT_TYPE (SELF::*pfMethodType)(PARAM_TYPE);
typedef RESULT_TYPE (*pfStaticInvokeFunc)(void* pObj, pfMethodType pFunc, PARAM_TYPE param);
template< typename TYPE, typename PARAM_TYPE, typename RESULT_TYPE >
class SaveClassType
{
typedef RESULT_TYPE (TYPE::* pfDelegateFunc)(PARAM_TYPE);
public:
static RESULT_TYPE Invoke(void* pObj, pfMethodType pFunc, PARAM_TYPE param)
{
return (static_cast<TYPE*>(pObj)->*((pfDelegateFunc)pFunc))(param);
}
};
public:
template <typename TYPE, typename PARAM_TYPE, typename RESULT_TYPE>
delegate2(TYPE* pObj, RESULT_TYPE (TYPE::* method)(PARAM_TYPE)):
m_pObj(pObj),
m_pFunc((pfMethodType)(method))
{
m_pfInvoke = SaveClassType<TYPE,PARAM_TYPE,RESULT_TYPE>::Invoke;
}
RESULT_TYPE operator()(PARAM_TYPE param)
{
assert(m_pfInvoke);
return (*m_pfInvoke)(m_pObj,m_pFunc,param);
}
private:
void* m_pObj;
pfMethodType m_pFunc;
pfStaticInvokeFunc m_pfInvoke;
};
It is also possible to create a class that'll do multiple parameters or rather variable parameters. But I'm not sure it's worth it because of the type safety issues it puts into play. It might be possible to code around that as well but I didn't have the time to mess with it...
Still here's a way to do it that relies on incomplete template instantiation - the complier won't add the template methods we don't use. That and yet more casting. I only added the possibility for 1 or 2 params, but zero..n could be added.
template< typename RESULT_TYPE >
class delegate_ex
{
typedef RESULT_TYPE (delegate_ex<RESULT_TYPE>::*pfMethodType)();
typedef RESULT_TYPE (*pfStaticInvokeFunc)(void* pObj,
pfMethodType pFunc,
void* param);
typedef RESULT_TYPE (*pfStaticInvokeFunc2)(void* pObj,
pfMethodType pFunc,
void* param1,
void* param2);
template< typename TYPE,
typename RESULT_TYPE ,
typename PARAM_TYPE >
class SaveClassType
{
typedef RESULT_TYPE (TYPE::* pfDelegateFunc)(PARAM_TYPE);
public:
static RESULT_TYPE Invoke(void* pObj,
pfMethodType pFunc,
void* param)
{
return (static_cast<TYPE*>(pObj)->*((pfDelegateFunc)pFunc))
(*static_cast<PARAM_TYPE*>(param));
}
};
template< typename TYPE,
typename RESULT_TYPE ,
typename PARAM_TYPE,
typename PARAM_TYPE2 >
class SaveClassType2
{
typedef RESULT_TYPE (TYPE::* pfDelegateFunc)
(PARAM_TYPE,PARAM_TYPE2);
public:
static RESULT_TYPE Invoke(void* pObj,
pfMethodType pFunc,
void* param1,
void* param2)
{
return (static_cast<TYPE*>(pObj)->*((pfDelegateFunc)pFunc))
(*static_cast<PARAM_TYPE*>(param1),
*static_cast<PARAM_TYPE2*>(param2));
}
};
public:
template <typename TYPE,
typename RESULT_TYPE,
typename PARAM_TYPE>
delegate_ex(TYPE* pObj, RESULT_TYPE (TYPE::* method)(PARAM_TYPE)):
m_pObj(pObj),
m_pFunc((pfMethodType)(method))
{
m_pfInvoke = SaveClassType<TYPE,
RESULT_TYPE,
PARAM_TYPE>::Invoke;
}
template <typename TYPE,
typename RESULT_TYPE,
typename PARAM_TYPE,
typename PARAM_TYPE2>
delegate_ex(TYPE* pObj,
RESULT_TYPE (TYPE::* method)(PARAM_TYPE,PARAM_TYPE2)):
m_pObj(pObj),
m_pFunc((pfMethodType)(method))
{
m_pfInvoke2 = SaveClassType2<TYPE,
RESULT_TYPE,
PARAM_TYPE,
PARAM_TYPE2>::Invoke;
}
template< typename PARAM_TYPE >
RESULT_TYPE operator()(PARAM_TYPE param)
{
assert(m_pfInvoke);
return (*m_pfInvoke)(m_pObj,m_pFunc,¶m);
}
template< typename PARAM_TYPE, typename PARAM_TYPE2 >
RESULT_TYPE operator()(PARAM_TYPE param1, PARAM_TYPE2 param2)
{
assert(m_pfInvoke);
return (*m_pfInvoke2)(m_pObj,m_pFunc,¶m1,¶m2);
}
private:
void* m_pObj;
pfMethodType m_pFunc;
union
{
pfStaticInvokeFunc m_pfInvoke;
pfStaticInvokeFunc2 m_pfInvoke2;
};
};
Although this class will work it is not very safe. Nothing prevents, for example:
struct F : public A
{
void func(int x)
{
...
}
void func2(int x, int y)
{
...
}
};
typedef delegate_ex<void> delegate_t;
std::list<delegate_t> delegate_list;
delegate_list.push_back(delegate_t(&e,E::func2));
delegate_list.push_back(delegate_t(&f,F::func2));
delegate_list.push_back(delegate_t(&f,F::func));
int y = 0;
for(i = delegate_list.begin();i != delegate_list.end();++i,y++)
{
(*i)(j++,y);
}
Worse things would happen given functions with signatures containing params of different types...
Non-variable params example is type safe as the correct casts are enforced.
Anyway, it's fun to play around with...
Mike
|
|
|
|
|
Mike Junkin wrote:
Anyway, here's a new delegate class that consolidates all the behavior into one class. It uses a templated static method in a nested class to 'save' the behavior required to do the proper cast.
Interesting solution indeed. It is type safe eventhough void pointers are used, much thanks to the template types.
You could optimize the code further by making pfStaticInvokeFunc m_pfInvoke; static. This'll save 4 bytes (depending on platform) per object.
I think I'll study this solution further when I get home.
Mike Junkin wrote:
It is also possible to create a class that'll do multiple parameters or rather variable parameters. But I'm not sure it's worth it because of the type safety issues it puts into play.
Yes, the type system is seriously in danger. I think it's only logical though that operator() 's with different parameter count should be in different interfaces. The number of parameters are indeed a part of the type signature. If you want to maintain type safety of any kind, then multiple interfaces is a must I'm afraid.
Woe to you, Oh Earth and Sea,for the Devil sends the beast with wrath, because he knows the time is short...
Let him who hath understanding reckon the number of the beast for it is a human number, it's number is Six hundred and sixty six
|
|
|
|
|
Jörgen Sigvardsson wrote:
You could optimize the code further by making pfStaticInvokeFunc m_pfInvoke; static. This'll save 4 bytes (depending on platform) per object
I don't think you can make the pointer static. If you do then every delegate, with the same combination of return/arguments, will cast itself as whatever the last created delegate points too. In your example everything would cast itself as a 'D' object.
It's that dynamic pointer that lets the delegates vary their callback class on a dynamic, per instance, basis.
Jörgen Sigvardsson wrote:
If you want to maintain type safety of any kind, then multiple interfaces is a must I'm afraid.
I agree with you. I included the multi-param option only to show it could be done. I don't think it's pratical due to the lack of type safety.
It's possible RTTI could be used to make it safe and useful. In fact RTTI would probably open up other alternatives to both of the classes.
Mike
|
|
|
|
|
Mike Junkin wrote:
I don't think you can make the pointer static. If you do then every delegate, with the same combination of return/arguments, will cast itself as whatever the last created delegate points too. In your example everything would cast itself as a 'D' object.
Doh! You're right. I forgot about the void pointer.
Mike Junkin wrote:
It's possible RTTI could be used to make it safe and useful. In fact RTTI would probably open up other alternatives to both of the classes.
Can you extract RTTI information about method/function signatures? If so, then yes, it would be possible. Allthough, the risk is high that one ends up with the "Java syndrome". All trapped "errors" would have to be guarded by throws - and this doesn't make the implementation exception neutral.
Woe to you, Oh Earth and Sea,for the Devil sends the beast with wrath, because he knows the time is short...
Let him who hath understanding reckon the number of the beast for it is a human number, it's number is Six hundred and sixty six
|
|
|
|
|
It's more than just a pointer. It's an object that contains a pointer to a class and a member function pointer. You can't call the member function of a class without an instance of an object. It's the same as a functor in STL.
Todd Smith
|
|
|
|
|
Russ Freeman wrote:
It's jolly difficult to have a pointer to a member function without 'this'
Actually no it isn't. The address of member functions are stored together with the class information - determined at compile time1. So it is possible to separate 'this' and methods. If you look under the hood of C++, you'll see that 'this' is merely an extra parameter in each member call. VC++ even has a call type for this - __thiscall.
1 With the exception of virtual member functions. These are stored in the virtual pointer table, also known as vtable. I'm not sure how delegates work on virtual methods, but I'm going to find out right now!
The Romulans are... beyond arrogant.
202.The justification of profit is profit.
|
|
|
|
|
Russ Freeman wrote:
What!?! Sure it is.
I must have misunderstood you. What I meant from all along was that you can separate the object from the method and later put them together to make a call.
Russ Freeman wrote:
In fact if you'd used the AT&T C++ front end compiler (I did circa 1993) then you could have looked at the intermediary C code and seen the *real* 'this' pointer.
I haven't had the pleasure to try any cfront compiler, that I'm aware of. Gcc, VC nor Borland C++ are not cfront compilers, or are they? I did however have the pleasure of splashing coffee all over a library copy of Stroustrups book "Design and Evolution of C++" - thus I had to buy a copy and replace the damaged one. Fortunately, it was still readable.
The Romulans are... beyond arrogant.
202.The justification of profit is profit.
|
|
|
|
|
It's not just a pointer to member function. It cloaks a part of the member function type. It also embedds a member function pointer and an object so that the caller will not need to "know about" the object at call time.
Since it hides the class-membership of the method pointer, you can store "similar" delegates in a type safe manner (std::map<>, std::list<>, etc).
So, it is not just a pointer to a member function.
The Romulans are... beyond arrogant.
202.The justification of profit is profit.
|
|
|
|
|
And for the record: I never claimed it would work with VC6
VC6 is terrible when it comes to template handling. My grand mother would be far more capable than the VC6 compiler.
The Romulans are... beyond arrogant.
202.The justification of profit is profit.
|
|
|
|
|
IIRC, this is legal C++:
void func() { }
void func2() {
return func();
}
Allthough, most compilers may not support it. But I'm sure as time goes on, compilers will be more compliant
to the C++ standards. I think VC7 is proof of that, and it's only getting better.
If the compiler you use does not support "returning voids", then you could use int as a dummy return type. It's a small sacrifice for making it work.
As I don't have VC6 installed, I cammot test this delegate pattern on that compiler. If the void is the only thing that's broken for
that compiler, then all is "fine and dandy" I suppose. If there is anything else broken, I'd love to hear about it and would be more than delighted if you gave me a patch or hints on making it work.
I plan on installing Cygwin later too, to see if the code works with gcc. I suspect though it will not work with gcc < 2.95. Judging by www.boost.org[^], gcc seems to be the most compliant compiler in production these days, so I'm confident it'll work there too.
The Romulans are... beyond arrogant.
202.The justification of profit is profit.
|
|
|
|
|
Jörgen Sigvardsson wrote:
IIRC, this is legal C++:
void func() { } void func2() { return func();}
Yep, full legal and standard behaviour
BTW , nice article Jörgen
Jörgen Sigvardsson wrote:
gcc seems to be the most compliant compiler in production these days
I think it isn't , the $50 Comeau compiler[^] is the most standard compiler , albeit it need c compilers backends to process the C generated code.
Cheers,Joao Vaz
And if your dream is to care for your family, to put food on the table, to provide them with an education and a good home, then maybe suffering through an endless, pointless, boring job will seem to have purpose. And you will realize how even a rock can change the world, simply by remaining obstinately stationary.-Shog9
|
|
|
|
|