Search code examples
c++templatesambiguityboost-log

Resolve ambiguousness of a base template class member


I have a hierarchy of classes and every one of them must have a particular base class. Than base class provides ability to post log records and takes in ctor name of a log channel (basically a name of a class which is using log). Lets call this class Logable.

To allow my classes to inherit from that Logable class several times, I gave it a template parameter and every descendant uses itself as this parameter.

Actually I'm using a boost::log library, but there is very simplified example of said hierarchy with simple LogableImpl class, which is replacing boost::log sink.

#include <iostream>
#include <string>

// macro for logging in a boost::log style
#define LOG_DEBUG this->_loggerObj.logStream("debug")
#define LOG_INFO  this->_loggerObj.logStream("info")
#define LOG_WARN  this->_loggerObj.logStream("warning")
#define LOG_ERROR this->_loggerObj.logStream("error")


class LogableImpl
{
private:
   std::string _channelName;
public:
   LogableImpl(const std::string & channelName): _channelName(channelName) {}

   std::ostream & logStream(const std::string & severetyLevel)
   {
      std::cout << _channelName << " " << severetyLevel;
      return std::cout;
   }
};


template <class Descendant>
class Logable
{
protected:
   Logable(const std::string & channelName): _loggerObj(channelName) {}
   LogableImpl _loggerObj;
};


class Base: private Logable<Base>
{
public:
   Base()
      : Logable<Base>("Base")
   {}

   void someMethod()
   {
      LOG_INFO << "some method is called" << std::endl;
      LOG_ERROR << "an error happened" << std::endl;
   }
};


class Derived: public Base, private Logable<Derived>
{
public:
   Derived()
      : Logable<Derived>("Derived")
   {}

   void someAnotherMethod()
   {
      LOG_INFO << "another method is called" << std::endl;
      LOG_ERROR << "another error is happened" << std::endl;
   }
};


int main()
{
   Base b;
   Derived d;
   b.someMethod();
   d.someMethod();

   return 0;
}

Obviously I've got an error from my compilation attempt of this source with MSVC 2008

error C2385: ambiguous access of '_loggerObj'
1>        could be the '_loggerObj' in base 'Logable<Base>'
1>        or could be the '_loggerObj' in base 'Logable<Derived>'
1>d:\cpp\visualstudio\tests\workbench\test\main.cpp(55) : error C2248: 'Logable<Descendant>::_loggerObj' : cannot access inaccessible member declared in class 'Logable<Descendant>'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>        d:\cpp\visualstudio\tests\workbench\test\main.cpp(29) : see declaration of 'Logable<Descendant>::_loggerObj'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>d:\cpp\visualstudio\tests\workbench\test\main.cpp(56) : error C2385: ambiguous access of '_loggerObj'
1>        could be the '_loggerObj' in base 'Logable<Base>'
1>        or could be the '_loggerObj' in base 'Logable<Derived>'
1>d:\prog\cpp\visualstudio\tests\workbench\boost_test\main.cpp(56) : error C2248: 'Logable<Descendant>::_loggerObj' : cannot access inaccessible member declared in class 'Logable<Descendant>'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>        d:\cpp\visualstudio\tests\workbench\test\main.cpp(29) : see declaration of 'Logable<Descendant>::_loggerObj'
1>        with
1>        [
1>            Descendant=Base
1>        ]
1>Build log was saved at "file://d:\cpp\visualStudio\tests\workbench\test\Debug\BuildLog.htm"
1>boost_test - 4 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

How can I specify right base member to use in a LOG_* macro? I feel that it can be done with some template magic, but just cant figure it out.

It must be done with MSVC2008, which does not support C++11x features


Solution

  • Unfortunately, I could not find a way to replace decltype to something that msvc 2008 could understand to use Petr's answer. Even boost::typeof were not suitable (as long as I used it correctly)

    So I came with a solution by adding a using case with a macro

    #include <iostream>
    #include <string>
    
    #define USE_APPROPRIATE_LOGGER(classname) using Logable<classname>::_loggerObj
    #define LOG_DEBUG _loggerObj.logStream("debug")
    #define LOG_INFO  _loggerObj.logStream("info")
    #define LOG_WARN  _loggerObj.logStream("warning")
    #define LOG_ERROR _loggerObj.logStream("error")
    
    class LogableImpl
    {
    private:
       std::string _channelName;
    public:
       LogableImpl(const std::string & channelName): _channelName(channelName) {}
    
       std::ostream & logStream(const std::string & severetyLevel)
       {
          std::cout << _channelName << " " << severetyLevel << " ";
          return std::cout;
       }
    };
    
    
    template <class Descendant>
    class Logable
    {
    protected:
       Logable(const std::string & channelName): _loggerObj(channelName) {}
       LogableImpl _loggerObj;
    };
    
    class Base: private Logable<Base>
    {
       USE_APPROPRIATE_LOGGER(Base);
    public:
       Base()
          : Logable<Base>("Base")
       {}
    
       void someMethod()
       {
          LOG_INFO << "some method is called" << std::endl;
          LOG_ERROR << "an error happened" << std::endl;
       }
    };
    
    class Derived: public Base, private Logable<Derived>
    {
       USE_APPROPRIATE_LOGGER(Derived);
    public:
       Derived()
          : Logable<Derived>("Derived")
       {}
    
       void someAnotherMethod()
       {
          LOG_INFO << "another method is called" << std::endl;
          LOG_ERROR << "another error is happened" << std::endl;
       }
    };
    
    
    int main()
    {
       Base b;
       Derived d;
       b.someMethod();
       std::cout << std::endl;
       d.someAnotherMethod();
    
       return 0;
    }
    

    Its ugly and kills idea of using inheritance to provide logability to class, but it seems that there is no other way without c++11