كيفية زيادة تحميل المشغل << هذا لا يستغرق أو يعيد ostream
-
08-07-2019 - |
سؤال
السؤال الأصلي
أنا أكتب فصل تسجيل حيث يكون الهدف هو أن أكون قادرًا على القيام بذلك:
// thread one
Logger() << "Some string" << std::ios::hex << 45;
// thread two
Logger() << L"Some wide string" << std::endl;
حاليًا يبدو رأس المسجل الخاص بي شيء من هذا القبيل:
#pragma once;
#include <ostream>
class Logger
{
public:
Logger();
~Logger();
std::ostream* out_stream;
};
template <typename T>
Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
بعض الملاحظات حول هذا الفصل:
- التوافق عبر النظام الأساسي ليس مشكلة.
- داخل logger.cpp هناك فئة مفردة تعتني بإنشاء Ostream "الحقيقي".
- يقوم مُنشئ المسجل والتفكيك بإغلاق القفل الضروري للفرد.
لدي ثلاث مشاكل:
- كيف أجعل المشغل << وظيفة صديق أو عضو حتى أتمكن من تعيين Out_stream كخاصة؟
- كيف أجعل المشغل << وظيفة تعمل للمتلاعبين؟
- كيف يمكنني إضافة تخصص بحيث إذا كان T arch* أو std :: wstring أنه سيقوم بتحويله إلى char* أو std :: string قبل نقله إلى Out_stream؟ (يمكنني القيام بالتحويل. إن فقدان أحرف Unicode العالية ليس مشكلة في حالتي.)
ملخص الأشياء المستفادة في الإجابات:
- ضع القالب أمام صديق بدلاً من بعد.
- STD :: iOS :: Hex ليس مناور. Std :: Hex هو مناور.
النتيجة النهائية
#pragma once
#include <ostream>
#include <string>
std::string ConvertWstringToString(std::wstring wstr);
class Logger
{
public:
Logger();
~Logger();
template <typename T>
Logger& operator<< (T data) {
*out << data;
return *this;
}
Logger& operator<< (std::wstring data) {
return *this << ConvertWstringToString(data);
}
Logger& operator<< (const wchar_t* data) {
std::wstring str(data);
return *this << str;
}
private:
std::ostream* out;
};
المحلول
يمكنك استخدام تعريف الصديق ، والذي سيحدد المشغل في مساحة الاسم المحيطة بالفصل ، وجعله مرئيًا فقط لدقة التحميل الزائد للمشغل (لا يمكن الاتصال بها يدويًا باستخدام :: Operator << ... بناء الجملة):
class Logger
{
public:
Logger();
~Logger();
std::ostream* out_stream;
template <typename T>
friend Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
/* special treatment for std::wstring. just overload the operator! No need
* to specialize it. */
friend Logger& operator<< (Logger& logger, const std::wstring & wstr) {
/* do something here */
}
};
البديل ، للحفاظ على الكود الخاص بك كما هو وجعل المشغل << قالب صديق ، يمكنك إضافة هذا السطر إلى تعريف الفصل الخاص بك:
template <typename T>
friend Logger& operator<< (Logger& logger, T thing);
لمشكلة مناور ، سأعطيك فقط الكود الذي أكتبه منذ بعض الوقت:
#include <iostream>
#include <cstdlib>
using namespace std;
template<typename Char, typename Traits = char_traits<Char> >
struct logger{
typedef std::basic_ostream<Char, Traits> ostream_type;
typedef ostream_type& (*manip_type)(ostream_type&);
logger(ostream_type& os):os(os){}
logger &operator<<(manip_type pfn) {
if(pfn == static_cast<manip_type>(std::endl)) {
time_t t = time(0);
os << " --- " << ctime(&t) << pfn;
} else
os << pfn;
return *this;
}
template<typename T>
logger &operator<<(T const& t) {
os << t;
return *this;
}
private:
ostream_type & os;
};
namespace { logger<char> clogged(cout); }
int main() { clogged << "something with log functionality" << std::endl; }
};
لاحظ أنه STD :: Hex ، ولكن ليس STD :: iOS :: Hex. يتم استخدام الأخير كعلم مناور ل setf
وظيفة التدفقات. لاحظ أنه على مثالك ، لا يلزم علاج خاص للتلاعب. هناك حاجة إلى المعاملة الخاصة أعلاه لـ STD :: ENDL فقط لأنني أقوم ببث الوقت بالإضافة إلى ذلك عند استخدام STD :: ENDL.
نصائح أخرى
استخدام قالب هو الطريقة الصحيحة للقيام بذلك ، ولكن عليك فقط التأكد من أن القالب في رأس ملف (logger.h
, ، أو أي شيء اتصلت به) ، ليس في ملف التنفيذ (logger.cpp
). سيعمل هذا تلقائيًا لأي نوع لديه operator <<
محددة مع std::ostream
. كما ستعمل تلقائيًا مع كائنات Manipulator Stream - تلك هي في الحقيقة مجرد وظائف تستغرق std::ostream
المعلمة ، و operator <<
فقط يدعو الوظيفة على ostream
.
تستطيع فعل operator <<
وظيفة صديق على النحو التالي:
template <typename T> friend Logger& operator<< (Logger& logger, T thing);
التخصصات سهلة - ما عليك سوى استخدام تخصصات القالب (مرة أخرى ، في ملف الرأس):
template <typename T>
Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
// Template specialization - the "template <>" part is necessary
template <>
Logger& operator<< (Logger& logger, const wchar_t *wstr)
{
// convert wstr to an ANSI string and log it
}
template <>
Logger& operator<< (Logger& logger, const std::wstring & wstr)
{
// convert wstr to an ANSI string and log it
}
لا يلزم إعلان الصداقة:
class Logger
{
public:
Logger();
~Logger();
template <typename T>
inline Logger& Display(T thing)
{
*out_stream << thing;
return *this;
}
private:
std::ostream* out_stream;
};
template <typename T>
Logger& operator<< (Logger& logger, T thing)
{
return logger.Display(thing);
}
لماذا لا تفعل ذلك طريقة printf واستخدام طريقة المعلمة متعددة (مع النقاط الثلاثة ...). لا يزال هذا يمنحك الكثير من القوة ولا يجعل الأمر فوضويًا كما هو الحال عند استخدام <<.
فمثلا:
Logger("This is my log msg %0X", 45);
شنق اثنين من secs وسوء سحب نموذج رمز لك.
يحرر:
void Logger(const char* format, ...)
{
char szMsg[3000];
va_list args;
va_start( args, format );
vsnprintf( szMsg, sizeof(szMsg) - 1, format, args );
va_end(args);
// code to print szMsg to a file or whatever here
}
إذا كنت ترغب في استخدام هذا كصف فئة وليس وظيفة قائمة بذاتها ، فيمكنك زيادة تحميل مشغل المسجل () وستعمل بنفس الشيء