[scope_exit] Boost.ScopeExit capturing 'this'

I wasn't sure whether I had the correct address for Boost.ScopeExit's author Alexander Nasonov, so I've submitted to this list just in case and to get feedback from other users. I have been playing with BOOST_SCOPE_EXIT -- it it very convenient. One thing I would have liked would be to capture 'this' inside member functions. I have created a patch which adds BOOST_SCOPE_EXIT_THIS to do just that. It provides an operator->() in the generated struct which can be used to access the enclosing class's members. The syntax is: BOOST_SCOPE_EXIT_THIS( (&a) (&b) ) { a; b; // local captures (*this)->member; // member access } BOOST_SCOPE_EXIT_END; and can be BOOST_SCOPE_EXIT_THIS() { (*this)->member; } BOOST_SCOPE_EXIT_END; providing the preprocessor allows for empty parameter lists (which gcc does and vc does albeit after disabling a warning to remove noise). The type pointed to by the result of operator->() is correctly cv qualified. It has been tested on gcc 4.3, gcc 4.2, msvc 7.1 and msvc 9.0. I don't have access to other compilers. Boost.Typeof worked fine on gcc for typeof(this) -- mostly because gcc has a native typeof I guess. On visual studio (vc71 and vc90) it was not so good -- a compiler bug ( http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?Feedbac... , http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?Feedbac... ) that has been marked as 'Won't fix' means that 'this' cannot be used in expressions within template argument lists -- even if those expressions are innocuous typeid()s or sizeof()s for type-inference. The common workaround (capturing a compile-time integer with an enumerator and using the enumerator in the template parameter list) doesn't work with the vc71-specific implementation of Boost.Typeof since it is based on typeid() -- this cannot be captured in a compile-time expression (or I know of no way to that doesn't involve the expression existing in a template argument list). All is not lost though -- the general-purpose sizeof() based approach for the other VC versions works fine for VC71 for determining the typeof 'this'. As such my patch includes a VC>=1300 specific workaround for typeof 'this' determination. This is unfortunate but I couldn't think of any other non-intrusive way of doing it. Please find the attached patch and let me know what you think -- I'm sure this feature would be useful to users -- it saves having to do: TypeOfMyClassWhichCouldBeTedious* self = this BOOST_SCOPE_EXIT( (&self) ) { self->member; } BOOST_SCOPE_EXIT_END; which removes the convenience of BOOST_SCOPE_EXIT in these contexts since something like: struct at_end { TypeOfMyClassWhichCouldBeTedious* self; ~at_end() { self->member; } } at_end = {this}; can be done without any dependencies to achieve the same result. Regards, Adam. ------------------------------------------------------------ This email and any attached files contains company confidential information which may be legally privileged. It is intended only for the person(s) or entity to which it is addressed and solely for the purposes set forth therein. If you are not the intended recipient or have received this email in error please notify the sender by return, delete it from your system and destroy any local copies. It is strictly forbidden to use the information in this email including any attachment or part thereof including copying, disclosing, distributing, amending or using for any other purpose. In addition the sender excludes all liabilities (whether tortious or common law) for damage or breach arising or related to this email including but not limited to viruses and libel. SELEX Communications Limited is a Private Limited Company registered in England and Wales under Company Number 964533 and whose Registered Office is Lambda House, Christopher Martin Rd, Basildon, SS14 3EL. England.

<adam.butcher <at> selex-comms.com> writes:
I wasn't sure whether I had the correct address for Boost.ScopeExit's author Alexander Nasonov, so I've submitted to this list just in case and to get feedback from other users.
I have replied to Adam but I have forgotten to add boost-devel to CC. Unfortunately, I don't access to my yandex account from work to forward the message now. Alex

Hi All, Alex is very busy at the moment so has asked me to forward my latest ScopeExit 'this capture' diff to the mailing list. This is the first chance I've had myself to do this so here it is. Hopefully we don't overlap with each other. The enhancement allows capturing of 'this' in the BOOST_SCOPE_EXIT capture sequence allowing access to any class members within the body. The current diff allows for the following opening syntaxes and has been trialled on gcc4.2, gcc4.3, vc7.1 and vc9.0. 1. BOOST_SCOPE_EXIT( (this) (&a) (&b) ) 2. BOOST_SCOPE_EXIT( (this) ) 3. BOOST_SCOPE_EXIT( (&a) (&b) ) 4. BOOST_SCOPE_EXIT() This avoids the current necessity of creating a temporary copy of 'this' just so that a reference may be passed as a scope exit argument; thus removing the convenience of scope exit since you must spell out the full type of 'pointer-to-enclosing-class' and name an instance of it prior to using BOOST_SCOPE_EXIT. The 'this' element is dealt with specifically by the preprocessor code in BOOST_SCOPE_EXIT and as such must appear as the first element in the sequence if it is to be captured. Member access for the enclosing class can be performed with the syntax: (*this) -- convertible to enclosing 'this' (**this) -- enclosing 'this' (*this)->member -- accesses member 'member' of enclosing 'this' This syntax is perhaps controversial though I think both Alex and myself are reasonably happy with it. Alternatives suggested were 'this_', 'this_()', 'enclosing_this', 'enclosing_this()', 'enclosing', 'enclosing()', 'self' and 'self()'. The versions of opening syntax where local capture sequences are empty (2. and 4. above) generate pre-processor warnings about insufficient arguments to macros on vc7.1 and vc9.0 which I have ignored by suppressing the warnings. I am sure that by extending or optimizing the use of Boost.PP it may be possible to remove them in all but the final case (4.) -- I am no Boost.PP expert so I'm sure there is a more efficient way of implementing 'this-capture' anyway. We would welcome any feedback from Boost.ScopeExit users. Regards, Adam The diff is attached and fragments of off-list mails are included below: Adam Butcher/UKMAIN/MM1 wrote on 02/03/2009 13:37:50:
Re: Boost.ScopeExit capturing 'this'
Hi Alex,
Adam Butcher/UKMAIN/MM1 wrote on 02/03/2009 08:33:42:
Re: Boost.ScopeExit capturing 'this'
Alexander Nasonov <alnsn@yandex.ru> wrote on 28/02/2009 19:31:52:
Re: Boost.ScopeExit capturing 'this'
Hi Adam, Thanks for the interest in improving ScopeExit. I agree that 'this' is imporant but I have no perfect solution for this so far.
_THIS suffix doesn't look nice to me.
Agreed, I don't like it either -- it was a fallback when I was beaten by the PP library (or my ability to program the pre-processor in general :-).
I would prefer some magic in BOOST_SCOPE_EXIT argruments to introducing a new macro. Off the top of my head:
BOOST_SCOPE_EXIT( (this)(&a)(b) ) // (this) must be first
I tried something like that and played with BOOST_SCOPE_EXIT( this (&a) (&b) ) and BOOST_SCOPE_EXIT( this ( (&a) (&b) ) ) also but couldn't figure out a way in the PP library to treat 'this' specially whilst ignoring it if the first sequence-item happened to be something other than this. I hope it is possible -- but I gave up after a number of close but ultimately insufficient attempts -- I will have another go.
Just had a play over lunch and have come up with something to allow a uniform interface for BOOST_SCOPE_EXIT( (this)(&a)(&b) ) and BOOST_SCOPE_EXIT( (&a)(&b) )
Here is my proof-of-concept for the mechanics:
---------------------------------------------------------------------------
#include <boost/preprocessor/library.hpp>
// should be innocuous as C++ standard doesn't permit this(...); make
// it equivalent anyway so as to generate appropriate error if used. #define this(x) (this(x))
#define X0 zero( #define X1 one(
#define zero(_) 0 #define one(_) 1
// Expand to 1 if the first sequence element is 'this' // or 0 otherwise. // // head = front(seq) --> 'this' or 'a' // funcexp = head(_) --> '(this(_))' or 'a(_)' // number_and_possible_tokens = seq_size(funcexp) // yields either // 0 a(_) // or 1 // expansion of 'cat(X,number_and_possible_tokens _) )' // yields either // X0 _ ) --> zero( _ ) --> 0 // or X1 tokens _ ) --> one( tokens _ ) --> 1 // #define TEST_FOR_ARG0_THIS(seq) \ BOOST_PP_CAT(X,BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_HEAD(seq)(_)) _) \ ) \ -- just for info: seq
TEST_FOR_ARG0_THIS( (this)(a)(b)(c) ) TEST_FOR_ARG0_THIS( (a)(b)(c) )
-- checks for 'this' behaviour --
this this(x) this()
---------------------------------------------------------------------------
This outputs (tested on gcc4.2, gcc4.3, msvc7.1 and msvc9.0)
1 -- just for info: (this)(a)(b)(c) 0 -- just for info: (a)(b)(c)
-- checks for 'this' behaviour --
this (this(x)) (this())
The result of TEST_FOR_ARG0_THIS(seq) can be used as a conditional, if 1 we pass in 'BOOST_PP_SEQ_TAIL(seq)' and 'this' into BOOST_SCOPE_EXIT_AUX_IMPL and if 0 we pass 'seq' and 'nothis' into it.
It should pan out okay -- it is pretty rude to make 'this(...)' a macro, though since C++ doesn't allow that syntax (that I'm aware of) it should be innocuous and will still generate an appropriate error if the user did, in error, use this(...).
What do you think? I'll have a go at integrating it into scope_exit.hpp (with the required identifier name changes) if I get time later -- if not I'll send a diff tonight.
Regards, Adam
Re: [boost] [scope_exit] Boost.ScopeExit capturing 'this'
I have replied to Adam but I have forgotten to add boost-devel to CC. Unfortunately, I don't access to my yandex account from work to forward the message now.
Alex
------------------------------------------------------------ This email and any attached files contains company confidential information which may be legally privileged. It is intended only for the person(s) or entity to which it is addressed and solely for the purposes set forth therein. If you are not the intended recipient or have received this email in error please notify the sender by return, delete it from your system and destroy any local copies. It is strictly forbidden to use the information in this email including any attachment or part thereof including copying, disclosing, distributing, amending or using for any other purpose. In addition the sender excludes all liabilities (whether tortious or common law) for damage or breach arising or related to this email including but not limited to viruses and libel. SELEX Communications Limited is a Private Limited Company registered in England and Wales under Company Number 964533 and whose Registered Office is Lambda House, Christopher Martin Rd, Basildon, SS14 3EL. England.

<adam.butcher <at> selex-comms.com> writes:
It should pan out okay -- it is pretty rude to make 'this(...)' a macro, though since C++ doesn't allow that syntax (that I'm aware of) it should be innocuous and will still generate an appropriate error if the user did, in error, use this(...).
I'm not an expert in a preprocessor but if we assume this(arg) macro is ok, then do(arg) must be ok as well: #include <boost/preprocessor/cat.hpp> #include <boost/scope_exit.hpp> #define BOOST_AUX_SCOPE_EXIT_later() BOOST_SCOPE_EXIT(BOOST_PP_EMPTY()) #define BOOST_AUX_SCOPE_EXIT_end() BOOST_SCOPE_EXIT_END #define do(when) BOOST_PP_CAT(BOOST_AUX_SCOPE_EXIT_,when)() int main() { do(later) { int i = 0; } do(end); // or do(ne) ? } Gcc 3.4 compiles this code. Alex

Hi Alex,
I'm not an expert in a preprocessor but if we assume this(arg) macro is ok, then do(arg) must be ok as well:
It is not OK: 17.4.3.1.1 Nor shall such a translation unit define macros for names lexically identical to keywords.

<snip>
All is not lost though -- the general-purpose sizeof() based approach for the other VC versions works fine for VC71 for determining the typeof 'this'. As such my patch includes a VC>=1300 specific workaround for typeof 'this' determination. This is unfortunate but I couldn't think of any other non-intrusive way of doing it.
The reason for using typeid instead of sizeof was to remove a limitation of the number of typeof instances you can have in a single compilation unit (~1000), but it should not not restricts the use of BOOST_TYPEOF, so I have reverted the use of typeid for BOOST_TYPEOF on VC7.1. It now uses the same sizeof solution as the other VC versions. Regards Peder

Peder Holt wrote on 10/03/2009 09:40:01:
<snip>
All is not lost though -- the general-purpose sizeof() based approach for the other VC versions works fine for VC71 for determining the typeof 'this'. As such my patch includes a VC>=1300 specific workaround for typeof 'this' determination. This is unfortunate but I couldn't think of any other non-intrusive way of doing it.
The reason for using typeid instead of sizeof was to remove a limitation of the number of typeof instances you can have in a single compilation unit (~1000), but it should not not restricts the use of BOOST_TYPEOF, so I have reverted the use of typeid for BOOST_TYPEOF on VC7.1. It now uses the same sizeof solution as the other VC versions.
I don't think that this will allow use of 'this' with BOOST_TYPEOF as is. I expect the following #include <boost/typeof/typeof.hpp> struct X { void f() { BOOST_TYPEOF(this) y = this; } }; would still fail due to the Microsoft bug cited earlier in this thread. It is to do with 'this' causing an erroneous compilation error when appearing inside an expression used as a template argument (even if that argument is only a sizeof() or typeid()). The work-around is to store, at compile-time, the result of the expression involving 'this', then use that compile-time constant as the argument to the template. This is why I needed to use the sizeof() approach in my ScopeExit msvc workaround -- as I don't know how one would 'store' the result of typeid (since it's the compile-time constant returned from typeid that is the significant value). With sizeof() its easy, just capture the result in an enumerator and then use the enumerator in place of the expression. I'm not sure how to make this generally useful for with BOOST_TYPEOF though since it requires a stand-alone statement to capture the type index (as far as I can tell). If this is necessary, I imagine it would restrict usage since you wouldn't be able to use BOOST_TYPEOF() 'inline' (for example in a template argument list). For BOOST_TYPEOF(this) to work on msvc (MS may fix it in vc10) I would imagine the msvc BOOST_TYPEOF would need to expand to something like: enum { boost_typeof_local_typeindex__LINE__ = ...expression-that-may-involve-this-to-get-type-index... }; ...expression-using-type-index-to-get-type... rather than ...expression-to-get-type... In Boost.ScopeExit 'this capture' the specific MSVC work-around to handle this was to have BOOST_SCOPE_EXIT_TYPEDEF_TYPEOF_THIS start a typedef resulting in something like: enum { boost_se_typeof_local_typeindex__LINE__ = ...expression-involving-this-to-get-type-index... }; typedef ...expression-using-type-index-to-get-type... which can then be followed with whatever identifier you want to capture the 'typeof(this)' into. If there is a way to combine the capture and usage into one expression then it'd be okay; or you could just say that msvc users are restricted to doing BOOST_TYPEOF(...) as a stand-alone statement. Bring on auto and decltype! Regards, Adam. ------------------------------------------------------------ This email and any attached files contains company confidential information which may be legally privileged. It is intended only for the person(s) or entity to which it is addressed and solely for the purposes set forth therein. If you are not the intended recipient or have received this email in error please notify the sender by return, delete it from your system and destroy any local copies. It is strictly forbidden to use the information in this email including any attachment or part thereof including copying, disclosing, distributing, amending or using for any other purpose. In addition the sender excludes all liabilities (whether tortious or common law) for damage or breach arising or related to this email including but not limited to viruses and libel. SELEX Communications Limited is a Private Limited Company registered in England and Wales under Company Number 964533 and whose Registered Office is Lambda House, Christopher Martin Rd, Basildon, SS14 3EL. England.

<snip>
For BOOST_TYPEOF(this) to work on msvc (MS may fix it in vc10) I would imagine the msvc BOOST_TYPEOF would need to expand to something like:
enum { boost_typeof_local_typeindex__LINE__ = ...expression-that-may-involve-this-to-get-type-index... }; ...expression-using-type-index-to-get-type...
rather than
...expression-to-get-type...
First of all, sorry for misquoting. The section I intended to quote was: The common workaround (capturing a compile-time integer with an enumerator and using the enumerator in the template parameter list) doesn't work with the vc71-specific implementation of Boost.Typeof since it is based on typeid() -- this cannot be captured in a compile-time expression (or I know of no way to that doesn't involve the expression existing in a template argument list). Second, thanks for the workaround. I have implemented this for BOOST_TYPEOF_NESTED_TYPEDEF, so that this can now be used with expressions containing this for VC compilers. Usage: BOOST_TYPEOF_NESTED_TYPEDEF(unique_name,this); typedef unique_name::type this_type; Can you use this in stead? Regards, Peder

Peder Holt <peder.holt <at> gmail.com> writes:
Second, thanks for the workaround. I have implemented this for BOOST_TYPEOF_NESTED_TYPEDEF, so that this can now be used with expressions containing this for VC compilers. Usage: BOOST_TYPEOF_NESTED_TYPEDEF(unique_name,this); typedef unique_name::type this_type;
Can you use this in stead?
Last time I looked, BOOST_TYPEOF_NESTED_TYPEDEF required a global scope while ScopExit requires a local scope. Alex
participants (4)
-
adam.butcher@selex-comms.com
-
Alexander Nasonov
-
Maxim Yanchenko
-
Peder Holt