2015-05-22 4 views
0

У меня есть иерархия классов, и каждый из них должен иметь определенный базовый класс. Чем базовый класс предоставляет возможность публиковать записи журнала и принимает имя ctor канала журнала (в основном имя класса, использующего журнал). Позволяет назвать этот класс Logable.Устранение неоднозначности базового члена класса шаблона

Чтобы позволить моим классам наследовать от этого класса Logable несколько раз, я дал ему параметр шаблона, и каждый потомок использует его как этот параметр.

На самом деле я использую библиотеку boost :: log, но есть очень упрощенный пример указанной иерархии с простым классом LogableImpl, который заменяет 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; 
} 

Очевидно, я получил сообщение об ошибке из моей компиляции попытки этого источника с 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 ========== 

Как я могу указать правильный элемент базы для использования в LOG_* макросе? Я чувствую, что это можно сделать с помощью магии шаблонов, но просто не могу понять это.

Это должно быть сделано с msvc2008, который не поддерживает C++ 11x функции

+0

Ну, какой '_loggerObj' вы хотите использовать в случае' Derived'? – Barry

+0

@Barry - тот, который находится в прямом предписании 'Logable' –

ответ

1

К сожалению, я не мог найти способ заменить decltype на то, что MSVC 2008 мог понять, к используйте ответ Петра. Даже boost::typeof не были пригодны (до тех пор, как я использовал его правильно)

Так что я пришел с решением, добавив using случай с макро

#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; 
} 

его уродливым и убивает идею использования наследования для обеспечения logability для класса, но, похоже, нет другого пути без C++ 11

+0

Да, я также думал об использовании 'using' для работы без C++ 11, просто не нашел времени для сделайте его работающим. – Petr

1

мне удалось это сделать с помощью C++ 11, явно указав, который Logable следует использовать. Поскольку мы не знаем тип this, я использую decltype:

#define LOGABLE_TYPE typename std::remove_reference<decltype(*this)>::type 
#define LOG_DEBUG this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("debug") 
#define LOG_INFO this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("info") 
#define LOG_WARN this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("warning") 
#define LOG_ERROR this->Logable<LOGABLE_TYPE>::_loggerObj.logStream("error") 

Посмотреть полный код здесь: http://ideone.com/1D5jrj

+0

Спасибо за ответ. К сожалению, я забыл упомянуть, что это должно быть сделано с MSVC 2008, который не поддерживает C++ 11x –