1

I have a base class Base, with many derived classes (eg. Derived1, Derived2). Base has a pure virtual function fn, which is called many times using a Base pointer. Every time the function is called, I need to do some extra logging and related stuff. In particular, I use BOOST_CURRENT_FUNCTION in the derived-class functions to find out which function was called. Is there a way to know this information before calling the function, so that I do not have to rewrite the bookkeeping code in every derived function?

Edit: I wish to avoid writing __PRETTY_FUNCTION__ in each derived function.

#include <iostream>
using namespace std;

class Base {
public:
virtual void fn() = 0;
};

class Derived1:public Base {
public:
void fn() {
cout<<__PRETTY_FUNCTION__<<endl;
}
};

class Derived2:public Base {
public:
void fn() {
cout<<__PRETTY_FUNCTION__<<endl;
}
};

int main()
{
    int choice =0;
    Base *ptr1 = nullptr;
    cout<<"Choose 0/1: "<<endl;
    cin>>choice;
    if(choice == 0) {    
       ptr1 = new Derived1;
    }else {
       ptr1 = new Derived2;
    }

    //********CAN I WRITE SOMETHING HERE, TO GIVE THE SAME RESULT?
    ptr1->fn();
}
SPMP
  • 1,181
  • 1
  • 9
  • 24
  • 4
    Can you post a small example of what you want to achieve, so we don't answer the "wrong" questions? – Mats Petersson Jan 27 '16 at 22:09
  • 1
    (I'm pretty sure, from the description of `BOOST_CURRENT_FUNCTION`, that it's just a wrapper for `__PRETTY_FUNCTION__` or something similar) – Mats Petersson Jan 27 '16 at 22:12
  • 2
    Make the method non virtual, let it do the book keeping and calling the actual virtual function, methodImpl. – erenon Jan 27 '16 at 22:13

5 Answers5

1

From your description it seems you migth have a design issue. Have you considered using the template method design patter? The idea is to have your base class implement the common functionality and through virtual functions implement the specifics in your derived classes.

LordDoskias
  • 3,121
  • 3
  • 30
  • 44
  • The problem is, it's not common functionality right? The functionality I want is to see which derived function is called. – SPMP Jan 27 '16 at 22:25
1

One idea is to implement the base pure virtual function and call it in each derived override. In the base one you increment a static counter. Something like:

#include <iostream>
#include <memory>

struct Base
{
    static size_t counter;
    virtual void f() = 0;
    virtual ~Base() = default;
};
size_t Base::counter{0};

void Base::f() // IMPLEMENTATION, yes it's possible to implement a pure virtual function
{
    ++counter;
}

struct Derived1: Base
{
    void f() override
    {
        Base::f(); // increment the counter
        std::cout << "Derived1::f()\n";
    }
};

struct Derived2: Base
{
    void f() override
    {
        Base::f(); // increment the counter
        std::cout << "Derived2::f()\n";
    }
};

int main()
{
    std::unique_ptr<Base> pBase1{new Derived1};
    std::unique_ptr<Base> pBase2{new Derived2};

    pBase1->f();
    pBase1->f();

    pBase2->f();

    std::cout << Base::counter << std::endl; // outputs 3
}

Live on Wandbox

If I'm not wrong I believe this is an instance of the Template Method design pattern mentioned by @LordDosias. There is no other intrinsic way of getting this information out from the language, as C++ does not have genuine runtime reflection capabilities.

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • I wouldn't call this the pure template method design pattern. But at the end of the day this is just semantics. In my opinion template method would be having Base::f() not be a pure virtual function, but an ordinary function which in turn calls private virtual method which are then implemented in the derived classes. But as I said whatever gets the job done :) – LordDoskias Jan 27 '16 at 22:22
  • @LordDoskias Yes I believe it's a matter of semantics. I knew about this from Scott Meyers' Effective C++, in which he was mentioning the fact that the common bookkeeping can be achieved by implementing a pure virtual function (which at the time I had no idea one can do). – vsoftco Jan 27 '16 at 22:24
  • um.. The idea is to have bookkeeping code only once (it's not just simple printing the function name). This approach doesn't help with that, right? – SPMP Jan 27 '16 at 22:29
  • @user2308211 Cannot you factor the bookkeeping code into `Base::f()`? If not, then you really have to do it manually in each derived override. – vsoftco Jan 27 '16 at 22:30
  • The bookkeeping code needs to know whether it's Derived1::f() or Derived2::f() – SPMP Jan 27 '16 at 22:31
  • @user2308211 In this case the only thing that comes to my mind is to somehow pass a parameter to `Base::f()` which tells you which `DerivedX` you are calling, and based on that parameter process it in `Base::f()`. The language does not offer intrinsic support for what you are trying to achieve. – vsoftco Jan 27 '16 at 22:34
  • I have a ``DerivedType`` enum member, which identifies what the derived type is, I'll have to fall back to that I suppose. – SPMP Jan 27 '16 at 22:37
  • 1
    I think you have a bigger design issue, because you essentially want your base class to know about each and every possible derive classes that you have. And this is not a good OOP design. Refactor your code so that you base doesn't care which derived is being called so long as it can just call a function which is overloaded in the derived class. – LordDoskias Jan 27 '16 at 23:32
  • I see your point, but for me, all I need is a function name, so that I can do stuff like log the amount of time taken in executing the call, memory allocated in the call etc.. – SPMP Jan 28 '16 at 04:55
1

No, it cannot be. C++ does not support this kind of introspection. __PRETTY_FUNCTION__ is all you're gonna get.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

Well, aside from wrapping your macro in another macro that is smaller/shorter/does more, there is nothing that will provide the name of a function for you.

   #define WHERE cout << __PRETTY_FUNCTION__ << endl
   ...
   void fn() {
      WHERE;
   }

This also means you can turn on/off the tracing trivially:

  #if TRACING
     #define WHERE cout << __PRETTY_FUNCTION__ << endl
  #else
     #define WHERE
  #endif

(You may want to wrap that in do { ... } while(0) in both sides to avoid problems if you were to put a WHERE inside an if, or some such, and still want it to work correctly when when it's "nothing")

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0

The simplest answer is that, since C++ doesn't have auxiliary methods, you have to split the implementation of fn into a utility wrapper and the virtual function proper:

class Base {
protected:
  virtual void fn_impl() = 0;
public:
  void fn() { fn_impl(); }
};

class BaseWithLogging: public Base {
public:
  void fn(); {
    /* do logging */
    fn_impl();
  }
};

If you want the logs to capture the exact identity of the virtual (function name, file, line number, ...) which is actually, then there is no workaround for that; the boilerplate has to go into the function.

The crusty old preprocessor can be of help. E.g. simple-minded illustration:

#define LOG (cout<<__PRETTY_FUNCTION__<<endl)

and then you just have

LOG;

at the beginning of the function.

Kaz
  • 55,781
  • 9
  • 100
  • 149