كيفية زيادة تحميل المشغل << هذا لا يستغرق أو يعيد ostream

StackOverflow https://stackoverflow.com/questions/290632

سؤال

السؤال الأصلي

أنا أكتب فصل تسجيل حيث يكون الهدف هو أن أكون قادرًا على القيام بذلك:

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

بعض الملاحظات حول هذا الفصل:

  1. التوافق عبر النظام الأساسي ليس مشكلة.
  2. داخل logger.cpp هناك فئة مفردة تعتني بإنشاء Ostream "الحقيقي".
  3. يقوم مُنشئ المسجل والتفكيك بإغلاق القفل الضروري للفرد.

لدي ثلاث مشاكل:

  • كيف أجعل المشغل << وظيفة صديق أو عضو حتى أتمكن من تعيين 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
}

إذا كنت ترغب في استخدام هذا كصف فئة وليس وظيفة قائمة بذاتها ، فيمكنك زيادة تحميل مشغل المسجل () وستعمل بنفس الشيء

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top