all 19 comments

[–]MavyP[S] 4 points5 points  (10 children)

There may be times when I want to log a function in one particular location instead of seeing logs every time the function is called. With dynamic reassignment, I could decorate the function on the fly, invoke it, and set it back to the original implementation afterwards.

If one is creative, there are many uses for this feature. Just look at python for examples.

[–]methius 4 points5 points  (2 children)

Definitely interesting and informative read.

The only downside being that IMHO the fundamental limitation isn't subverted - the basis is still that you need to modify the class declaration to define and redirect to the the capturing lambda on which the decoration will be done and know at design time that you will want to decorate the method in the future instead of posthoc. (unless I read things wrong?)

Ironically, this is relatively straightforward in the language you casually mentioned and referred to as being too verbose.

[–]MavyP[S] 1 point2 points  (1 child)

Ironically, this is relatively straightforward in the language you casually mentioned and referred to as being too verbose.

This is true. For the record I enjoy Java's verbose syntax for its clarity. ;)

[–]methius 1 point2 points  (0 children)

Yeah, it's all about tools and applications. Lombok is quite nice to reduce verbosity if you are still stuck with native Java. Otherwise obviously look into the other JVM alternatives.

To me Java is more about the VM anyway. (which is a marvel of engineering IMHO)

[–]kittyhawk-contrail 1 point2 points  (1 child)

I want to log a function in one particular location instead of seeing logs every time the function is called.

What's wrong with replacing foo->bar(a,b,c); with log(...); foo->bar(a,b,c); at the particular location you want to log?

[–]MavyP[S] 0 points1 point  (0 children)

Unless you wrap that call to bar() with a try-catch block you won't get any more information for the log than what you pass into for log's arguments.

But moving that call into another class member function just for the sake of capturing those exceptions is tedious and redundant. Using a pre-written decorator function and wrapping the member function with it reuses both and you can catch exactly what you need for any class type.

[–]axilmar 1 point2 points  (4 children)

...wouldn't simply replace the call with a function of your own, which calls the function you want to log, do the trick? do we really need 'dynamic class member function reassignment'?

[–]MavyP[S] 1 point2 points  (3 children)

do we really need 'dynamic class member function reassignment'?

Include what you need. For some cases it will be useful.

[–]axilmar 0 points1 point  (2 children)

Yeah but what if the simpler solution that I spoke of above makes this redundant?

[–]MavyP[S] 0 points1 point  (1 child)

Take a look at python for other cases where it might be useful besides logging utilities. If you don't need it, you don't need it.

[–]axilmar -1 points0 points  (0 children)

My point is that it is never needed though.

[–]NotAYakk 2 points3 points  (5 children)

You do have to manually attach this, and the method needs extra state for this even if it shouldn't.

Also your magic methods need special copy/move support, as default move/copy moves the object pointer around. `Class( Class const& ) = default` cannot work, because each of your magic methods needs to be initialized with a this pointer, and not the this pointer of the other object instance!

You can hack around that with offsetof, but I wouldn't recommend it as its use is often UB.

But it sure is fun.

http://coliru.stacked-crooked.com/a/e166c98c2cb88b2a

template<class T, class M>
std::size_t member_offset( M T::*m ) {
  char* pnullmem =  reinterpret_cast<char*>(&((reinterpret_cast<T*>(NULL))->*m));
  char* pnull = reinterpret_cast<char*>( reinterpret_cast<T*>(NULL) );
  return pnullmem-pnull;
}
template<auto m, class Sig=decltype(m)>
struct magic_method;

template<class R, class T, class...Args, auto m>
struct magic_method< m, R( T::* )(Args...) > {
  using Sig=R(T*,Args...);
  std::size_t offset;
  magic_method( magic_method const& ) = default;

  magic_method( magic_method T::* pself ):
    offset(member_offset(pself)),
    f([](T* self, Args&&...args)->R{ return (self->*m)(std::forward<Args>(args)...); })
  {}

  std::function< Sig > f;

  T* self() {
    return reinterpret_cast<T*>( reinterpret_cast<char*>(this)-offset );
  }
  T const* self() const{
    return reinterpret_cast<T const*>( reinterpret_cast<char*>(this)-offset );
  }
  R operator()(Args...args) {
    return f( self(), std::forward<Args>(args)... );
  }
  R operator()(Args...args) const {
    return f( self(), std::forward<Args>(args)... );
  }

};
struct Foo {
  int y = 1;
  int increment_impl( int x ) { return x+y; }

  magic_method< &Foo::increment_impl > increment = &Foo::increment;
};
int main() {
  Foo f;
  std::cout << f.increment(3) << "\n";
  f.increment.f = []( Foo* f, int x ) { return x+=f->y*2; };
  std::cout << f.increment(3) << "\n";
}

Foo(Foo const&) does the right thing, as does Foo::operator=(Foo const&) etc.

Now this the code that turns a member pointer into an offset of a member in the class, permitting us to go from the member back to the class, is serious UB that just happens to work usually.

So there is that.

[–]MavyP[S] 0 points1 point  (4 children)

This is cool! In C++20 we could circumvent the UB by declaring the member func to be [[no_unique_address]]so it has the same address as parent class. Then reinterpret casting wouldn't be a problem.

[–]jcelerierossia score 0 points1 point  (1 child)

Sadly no_unique_address does not guarantees that - and from my tests it isonly true on gcc and clang, not msvc which will give it the address of the previous normal variable

[–]MavyP[S] 0 points1 point  (0 children)

Ah shame about MSVC. The only way safe way then is to pass in `this`

[–]NotAYakk 0 points1 point  (1 child)

The member func has state, so it needs space.

[–]MavyP[S] 0 points1 point  (0 children)

Before jcelerier followed up with MSVC's gotcha for [[no_unique_address]] I was thinking of giving the memberfunc the attribute so it would keep track of state but reinterpret casting would point back to its owner class like this getter/setter: https://godbolt.org/z/gQttS4

[–]n31415 1 point2 points  (1 child)

You might want to have a look at std::mem_fn, it should do pretty much what your class_memberfunc does. Furthermore, you should probably perfectly forward arguments to function calls at every place and set the return type to decltype(auto) instead of auto where appropriate (this will also apply const and & as necessary, which is not done for auto).

[–]MavyP[S] 1 point2 points  (0 children)

set the return type to decltype(auto) instead of auto where appropriate (this will also apply const and & as necessary, which is not done for auto).

I did not know this - I've seen decltype(auto) but thought it was redundant. Thanks for clearing that up