views:

293

answers:

2

Hello guys, I have some code which use 'this' pointer of class which calls this code. For example:

Some::staticFunction<templateType>(bind(FuncPointer, this, _1));

Here is I'm calling bind function from boost. But it doesn't matter. Now I have to wrap this code. I made a macro:

#define DO(Type, Func) Some::staticFunction<Type>(bind(FuncPointer, this, _1));

And compiler insert this code into class which calls this macro, so 'this' takes from caller. But I don't want to use macro and prefer function (inline). But how to resolve 'this' passing. Could I use it in inline function like in macro or I have to pass it manually?

+3  A: 

An inline function is a regular function and must be compilable by itself. The inline keyword is simply a suggestion to the compiler to put the actual function body where the function is called, for optimization reasons. In the specific an inline function cannot access names that are visible only at the place of calling (in your case "this"), so you must pass them as parameters.

On the other hand a macro is just a text expansion happening at preprocessing time, so the macro body is not required to have a meaning by itself and everything is ok as long as the final expansion you end up with is legal C++ code.

For example you can have a macro "opening" a for loop and another macro "closing" it (something that clearly an inline function cannot do).

6502
+5  A: 

The this keyword can be put by the function you call

class MyClass {
  // ...
  template<typename Type, typename Func>
  void doit(Func f) {
    Some::staticFunction<Type>(bind(f, this, _1));
  }
};

After which you can call

doit<templateType>(FuncPointer);

If you like you can inherit the function

// T must be a derived class of MyRegister<T>
template<typename T>
class MyRegister {
protected:
  template<typename Type, typename Func>
  void doit(Func f) {
    Some::staticFunction<Type>(bind(f, (T*)this, _1));
  }
};

class MyClass : MyRegister<MyClass> {
  // ...
};

That way you can just use doit and not first write it, just like with the macro. Useful if you have a wide range of classes that you use the function in.

Edit: Notice that the C-Style cast is required here (can't use static_cast), because of the private inheritance. It's a safe cast if T is derived from MyRegister<T>


Personally i would prefer this over the macro. Notice that your macro can't cope with commas in the type-name

DO(std::pair<A, B>, g);

This tries to pass 3 arguments to the macro instead of 2. You could reverse the order of the type and the function and use variadic macros (which are a C++0x feature, but available in some compiler in C++03 mode) or you could use a typedef and pass the alias name.

Johannes Schaub - litb
Seems like you've given `this` an entirely different meaning from what it had in the original code. C++ doesn't have anything like upvar http://www.icsi.berkeley.edu/~sather/Documentation/Gui/TclTkDocs/TclCmd/upvar.n.html
Ben Voigt
@Ben i don't know Tcl, but what i've done doesn't change the value of `this`: Inserting intermediary function calls won't affect its value. In the original code, `this` refers to the current object, and in my code that's the case too. Notice that i'm not proposing to pass `doit` to `bind`. But I'm proposing to replace the macro call by a call to `doit`.
Johannes Schaub - litb
Now I see. You're putting the helper method into the class where the caller is... I guess it works, but it impacts the header file and the public API of the caller class, where the other presumably only affected the implementation file. Oh well.
Ben Voigt
@Ben, the helper is private and can be defined in the implementation file (since it's only used there) so it doesn't affect the public interface.
Johannes Schaub - litb
The inheritance list appears in the class declaration, and I don't think base classes can be incomplete types either. Templates can't be hidden away in the implementation file either, unless you can predict and explicitly instantiate every used combination of arguments.
Ben Voigt
@Ben the the class declaration shouldn't serve as declaration. Doxygen or something else should, and it doesn't (or shouldn't) include privately inherited classes. I think this is a fine application of templates :) But I wouldn't do the inheritance, but just define the function template myself (in the .cpp file). The benefit will be there even if i declare it myself for many applications of the call. And for only a few calls, i don't need the function in the first case. I showed the inheritance solution to present an even more lazy way :)
Johannes Schaub - litb
first - DO((std::pair<A,B>),g); second - "[T]he class declaration shouldn't serve as declaration." 0_o??
Noah Roberts
-1 for use of c-style casting, which can actually be a very, very big deal with this kind of code.
Noah Roberts
@Nah Roberts, i *have* to use C-Style casting because of the private inheritance. You can assure the inheritance relationship of T with ìs_base_of` from boost, but you can't get around the downcast using a C-Style cast, because `static_cast` can't cast across it and adding a `friend` declaration for the CRTP base-class is disturbing. The Cast with the C-Style cast is perfectly well-defined, as it disregards accessibility. Also your proposed solution of parenthesizing the type-name won't work.
Johannes Schaub - litb
"*have to use C style casting*" Really? Isn't the C style cast identical to `static_cast<T*>(static_cast<void*>(this))`, except that it also silently strips cv-qualifiers? IOW, it is undefined behavior, but for real-world compilers will work as long as `MyRegister<MyClass>` is the left-most base class.
Ben Voigt
Well, this answer suggests Johannes is absolutely right: http://stackoverflow.com/questions/844816/c-style-upcast-and-downcast-involving-private-inheritance/845076#845076
Ben Voigt
Just note that you can use Boost preprocessor library to make this much easier- for example, BOOST_PP_COMMA() to replace , to pass in the right arguments.
DeadMG
@DeadMG good point there. Another trick is to actually pass with `(pair<A, B>)` and then put `void` in front to build a function type out of it. Then one can pass it to a template to get the type of the first parameter. This works for many types, although fails for arrays and function types (both do decay) and fail for the void type.
Johannes Schaub - litb
@Noah it's customary to retract your downvote once you found you were mistaken, or to comment on why you keep the downvote
Johannes Schaub - litb
I will when and if you provide the safety measures necessary to guarantee correct use of an unsafe construct. Although I am inclined to leave it anyway since I hate being hassled about stupid crap just because I don't respond IMMEDIATELY during the weekend. Besides, I never said to use static_cast.
Noah Roberts
@Noah: Do you want him to check if every pointer is NULL before accessing it? Safe use of unsafe constructs is inherent in every C++ application.
DeadMG
@Dead - There are levels of safety. It would actually be fairly trivial to protect the C-style cast version from misuse during instantiation, especially compared to the amount of debugging it would save when someone inadvertently violates what amounts to an internal requirement (UB is almost always the worst kind of itch). Even if yes, you have to check for 0 every time (though once would be sufficient in this case), you are better off doing that than in summoning UB.
Noah Roberts
@Noah compared to the already installed `protected`, any bulk of code using `is_base_of` or similar is just noise around the point. You basically cannot accidentally misuse that function, i think. And if you do, the `bind` call will fail because the member pointer won't be compatible with the `this` pointer. Anything beyond is *deliberate* misuse. Wrt "Even if yes, you have to check for 0 every time, you are better off doing that than in summoning UB": http://www.gotw.ca/conv/002.htm . Trying to check against deliberate misuse is bad.
Johannes Schaub - litb
Trying to check against *deliberate* misuse is bad. Checking against accidental misuse is good. If your example were inherited publicly you could use a static_cast, no? In all reasonable uses? In fact the answer is no. You would only be able to use a static_cast *iff* type T is a direct descendant and there ARE reasonable uses that defy that requirement. Any such use would cause UB here and as such the precondition of your construct really should be validated. This isn't like the GOTW example that explains why checking that UB hasn't already been invoked is unreasonable and impossible.
Noah Roberts
+1: Fascinating. I didn't know you could static cast to an inaccessible base using a C-style cast.
James McNellis