2013-02-12 3 views
2

У нас есть реализация журнала, основанная на std :: ostream с настраиваемым буфером потока. Мы реализуем экземпляр нашего приложения класса журнала через счетчик Schwarz.Наследование от класса C++ без виртуальных функций

Чтобы избежать связывания наших классов нижнего уровня с нашей реализацией журнала, мы можем передать ссылку на std :: ostream. Таким образом, наши классы нижнего уровня могут входить в std :: cout, std :: cerr или в экземпляр, созданный с помощью счетчика Schwarz.

У меня есть одна проблема с этим. Реализация журнала устанавливает его серьезность через перегрузку оператора потока:

// Overload the << operator to set the log message severity 
inline CLogStream& operator << (CLogStream& myLogStream, eMsgType::type msgTypeCurrent) 
{ 
    myLogStream.SetMsgTypeCurrent(msgTypeCurrent); 
    return (myLogStream) ; 
} 

Это позволяет использовать регистратор как это:

CLog::Main << CLog::MSG_FATAL << "Fatal error" << std::endl; 

Я хотел бы создать ссылку на экземпляр нашего приложения журнала, который заблокирован для конкретной серьезности. Таким образом, я могу передать нашим классам утилиты две ссылки std :: ostream. Один из них будет использоваться для обычной отчетности, а другой для сообщений об ошибках. Они могут быть установлены в std :: cout и std :: cerr или на какой-то объект, ссылающийся на наш экземпляр объекта журнала.

К сожалению, оператор std :: ostream < < не является виртуальным, насколько я знаю, поэтому я не уверен, как создать такой объект.

Любые мысли?

+0

Если 'SetMsgTypeCurrent' не является' virtual' (т. Е. 'CLogStream' не предназначен для наследования), тогда это сложно. – Jon

+0

@Jon: Я могу изменить определение SetMsgTypeCurrent, поэтому может сделать его виртуальным, если это поможет. Но мне нужно получить доступ к классу через std :: ostream, поэтому я не вижу, какие параметры могли бы дать мне. Единственные виртуальные функции, о которых я знаю, находятся в std :: streambuf, и ни одна из них не кажется полезной для этой цели. –

+0

Не ответ, но: я вижу и другие проблемы с этим дизайном. В частности, он не является потокобезопасным: другой поток может вмешиваться между «<< CLog :: MSG_FATAL» и «<<» фатальной ошибкой ». Меняет дизайн полностью вариант? – jogojapan

ответ

1

iostream имеет функции виртуальных членов (в частности, ~ios_base), чтобы вы могли выполнить dynamic_cast в operator<<:

inline std::ostream &operator<<(std::ostream &os, eMsgType::type msgTypeCurrent) { 
    if (CLogStream *myLogStream = dynamic_cast<CLogStream *>(&os)) { 
    myLogStream->SetMsgTypeCurrent(msgTypeCurrent); 
    } else { 
    os << "TYPE: " << static_cast<typename std::underlying_type<eMsgType::type> 
     ::type>(msgTypeCurrent) << ": "; 
    } 
    return os; 
} 
+0

Это довольно дорого! –

+0

@PeterWood 'dynamic_cast' будет иметь некоторые накладные расходы, но для ведения журнала в нем, вероятно, будет доминировать IO. Прибор кода покажет, так ли это. – ecatmur

+0

Разве этот оператор не должен подвергаться нашему классу низкого уровня вместе с по крайней мере некоторыми частями класса журнала? Я хотел бы удалить все знания о классе журнала из классов низкого уровня. –

1

Если установка тяжести является постоянным, таким образом, что обе TESE линии приводит к записи в журнале со смертельным суровости

CLog::Main << CLog::MSG_FATAL << "Log entry 1: " << some_data << std::endl; 
CLog::Main << "Log entry 2: " << some_other_data << std::endl; 

тогда ваш класс протоколирования уже правильно разработан, чтобы быть переданы вместе как общий ostream&. Вам просто нужны отдельные экземпляры журнала для разных уровней журналов, которые поддерживаются классами утилиты.

Это написано в предположении, что класс регистратора был унаследован от ostream, чтобы воспользоваться существующими перегрузками operator<<. В этом случае operator<< для some_data и some_other_data уже полностью не знают, что выход идет в поток журнала.

+0

Это сработает, но я надеялся использовать один и тот же экземпляр везде. Пользователи могут добавлять различные параметры интерфейса вывода в экземпляр журнала (файл, UDP, stdout и т. Д.) - они не будут присутствовать в новых экземплярах, если мы не добавим какой-либо метод клонирования. –

+0

@SimonElliott: Проблема с одним экземпляром заключается в том, что этот экземпляр не может определить, на каком уровне должен произойти следующий вывод. С другой стороны, пользователи должны применять различные варианты для разных экземпляров. –

+0

Возможно, все они могут использовать поток буфера: вот где происходит большая часть реальной работы. –

1

Подробнее о ios_base::iword(). Это дает вам доступ к массиву значений long в объекте потока, который вы можете использовать для хранения таких вещей, как флаги и специальные значения.

Смежные вопросы