เรียกคืนสถานะของ std :: cout หลังจากจัดการมัน


106

สมมติว่าฉันมีรหัสดังนี้:

void printHex(std::ostream& x){
    x<<std::hex<<123;
}
..
int main(){
    std::cout<<100; // prints 100 base 10
    printHex(std::cout); //prints 123 in hex
    std::cout<<73; //problem! prints 73 in hex..
}

คำถามของฉันคือมีวิธีใดในการ 'กู้คืน' สถานะกลับเป็นสถานะcoutเดิมหลังจากกลับจากฟังก์ชันหรือไม่? (ค่อนข้างเหมือนstd::boolalphaและstd::noboolalpha.. )?

ขอบคุณ.


ฉันเชื่อว่าฐานสิบหกคงอยู่สำหรับการดำเนินการกะครั้งถัดไป การเปลี่ยนแปลงจะคงอยู่ต่อไปหากคุณเปลี่ยนแฟล็กรูปแบบด้วยตนเองแทนที่จะใช้ตัวปรับแต่ง
Billy ONeal

4
@BillyONeal: ไม่การใช้หุ่นยนต์จะมีผลเหมือนกับการเปลี่ยนแฟล็กรูปแบบด้วยตนเอง :-P
Chris Jester-Young

3
ถ้าคุณอยู่ที่นี่เนื่องจากการ Covertiy หาไม่การคืนค่ารูปแบบ ostream (STREAM_FORMAT_STATE)แล้วเห็นCoverity ค้นพบ: ไม่การคืนค่ารูปแบบ ostream (STREAM_FORMAT_STATE)
jww

ผมทำอะไรที่คล้ายกัน - ดูคำถามของฉันเกี่ยวกับรหัสตรวจสอบ: การใช้กระแสมาตรฐานและเรียกคืนการตั้งค่าในภายหลัง
Toby Speight

1
คำถามนี้เป็นตัวอย่างที่สมบูรณ์แบบว่าทำไม iostream จึงไม่ดีไปกว่า stdio เพิ่งพบข้อบกพร่องที่น่ารังเกียจสองข้อเนื่องจาก iomanip ที่ไม่ / กึ่ง / เต็ม / สิ่งที่ไม่ถาวร
fuujuhi

คำตอบ:


99

คุณต้องการ#include <iostream>หรือ#include <ios>เมื่อจำเป็น:

std::ios_base::fmtflags f( cout.flags() );

//Your code here...

cout.flags( f );

คุณสามารถใส่เหล่านี้ที่จุดเริ่มต้นและจุดสิ้นสุดของการทำงานของคุณหรือตรวจสอบคำตอบนี้เกี่ยวกับวิธีการใช้วิธีนี้กับRAII


5
@ ChrisJester-Young C ++ ที่ดีจริงคือ RAII โดยเฉพาะอย่างยิ่งในกรณีเช่นนี้!
Alexis Wilke

4
@ Alexis ฉันเห็นด้วย 100% ดูคำตอบของฉัน (Boost IO Stream State Saver) :-)
Chris Jester-Young

3
สิ่งนี้ไม่ปลอดภัยยกเว้น
einpoklum

2
สถานะสตรีมยังมีอีกมากนอกเหนือจากแฟล็ก
jww

3
คุณสามารถหลีกเลี่ยงปัญหาได้โดยการไม่ผลักดันรูปแบบไปยังสตรีม พุชรูปแบบและข้อมูลลงในตัวแปร
สตริงสตรีม

63

Boost IO กระแส Saver รัฐดูเหมือนว่าสิ่งที่คุณต้องการ :-)

ตัวอย่างตามข้อมูลโค้ดของคุณ:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}

1
โปรดทราบว่าที่นี่ไม่มีเวทมนตร์ios_flags_saverเพียงแค่บันทึกและตั้งค่าสถานะเหมือนในคำตอบของ @ StefanKendall
einpoklum

15
@einpoklum แต่ปลอดภัยเป็นพิเศษไม่เหมือนคำตอบอื่น ๆ ;-)
Chris Jester-Young

2
สถานะสตรีมยังมีอีกมากนอกเหนือจากแฟล็ก
jww

4
@jww ไลบรารี IO Stream State Saver มีหลายคลาสสำหรับการบันทึกส่วนต่างๆของสถานะสตรีมซึ่งios_flags_saverเป็นเพียงส่วนเดียว
Chris Jester-Young

3
หากคุณคิดว่ามันคุ้มค่าที่จะนำกลับมาใช้ใหม่และบำรุงรักษาทุกสิ่งด้วยตัวเองแทนที่จะใช้ห้องสมุดที่ผ่านการตรวจสอบและผ่านการทดสอบอย่างดี ...
jupp0r

46

std::coutหมายเหตุว่าคำตอบที่นำเสนอนี้จะไม่เรียกคืนสภาพเต็มรูปแบบของ ตัวอย่างเช่นstd::setfillจะ "ติด" แม้จะโทร.flags()แล้วก็ตาม ทางออกที่ดีกว่าคือใช้.copyfmt:

std::ios oldState(nullptr);
oldState.copyfmt(std::cout);

std::cout
    << std::hex
    << std::setw(8)
    << std::setfill('0')
    << 0xDECEA5ED
    << std::endl;

std::cout.copyfmt(oldState);

std::cout
    << std::setw(15)
    << std::left
    << "case closed"
    << std::endl;

จะพิมพ์:

case closed

ค่อนข้างมากกว่า:

case closed0000

แม้ว่าคำถามเดิมของฉันจะได้รับคำตอบเมื่อไม่กี่ปีที่ผ่านมา แต่คำตอบนี้เป็นส่วนเสริมที่ดี :-)
UltraInstinct

2
@UltraInstinct ดูเหมือนว่าจะเป็นทางออกที่ดีกว่าในกรณีนี้คุณสามารถทำได้และน่าจะทำให้เป็นคำตอบที่ยอมรับแทน
underscore_d

ด้วยเหตุผลบางประการทำให้เกิดข้อยกเว้นหากเปิดใช้งานข้อยกเว้นสำหรับสตรีม coliru.stacked-crooked.com/a/2a4ce6f5d3d8925b
anton_rh

1
ดูเหมือนว่าstd::iosจะอยู่ในสภาพแย่เสมอเพราะมีNULLrdbuf ดังนั้นการตั้งค่าสถานะที่เปิดใช้งานข้อยกเว้นทำให้เกิดการโยนข้อยกเว้นเนื่องจากสถานะไม่ดี วิธีแก้ไข: 1) ใช้คลาสบางอย่าง (เช่นstd::stringstream) กับrdbufset แทนstd::ios. 2) บันทึกสถานะข้อยกเว้นแยกจากตัวแปรภายในและปิดใช้งานก่อนหน้าstate.copyfmtนี้จากนั้นเรียกคืนข้อยกเว้นจากตัวแปร (และทำอีกครั้งหลังจากกู้คืนสถานะoldStateที่ปิดใช้งานข้อยกเว้น) 3) ตั้งค่าrdbufเป็นstd::iosแบบนี้:struct : std::streambuf {} sbuf; std::ios oldState(&sbuf);
anton_rh

22

ฉันได้สร้างคลาส RAII โดยใช้โค้ดตัวอย่างจากคำตอบนี้ ข้อได้เปรียบที่ยิ่งใหญ่ของเทคนิคนี้เกิดขึ้นหากคุณมีเส้นทางส่งคืนหลายเส้นทางจากฟังก์ชันที่ตั้งค่าสถานะบน iostream ไม่ว่าจะใช้เส้นทางใดกลับตัวทำลายจะถูกเรียกและแฟล็กจะถูกรีเซ็ตเสมอ ไม่มีโอกาสที่จะลืมคืนค่าแฟล็กเมื่อฟังก์ชันส่งคืน

class IosFlagSaver {
public:
    explicit IosFlagSaver(std::ostream& _ios):
        ios(_ios),
        f(_ios.flags()) {
    }
    ~IosFlagSaver() {
        ios.flags(f);
    }

    IosFlagSaver(const IosFlagSaver &rhs) = delete;
    IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete;

private:
    std::ostream& ios;
    std::ios::fmtflags f;
};

จากนั้นคุณจะใช้มันโดยสร้างอินสแตนซ์ท้องถิ่นของ IosFlagSaver เมื่อใดก็ตามที่คุณต้องการบันทึกสถานะแฟล็กปัจจุบัน เมื่ออินสแตนซ์นี้อยู่นอกขอบเขตสถานะแฟล็กจะถูกเรียกคืน

void f(int i) {
    IosFlagSaver iosfs(std::cout);

    std::cout << i << " " << std::hex << i << " ";
    if (i < 100) {
        std::cout << std::endl;
        return;
    }
    std::cout << std::oct << i << std::endl;
}

2
ยอดเยี่ยมถ้ามีคนขว้างคุณยังคงได้รับธงที่ถูกต้องในสตรีมของคุณ
Alexis Wilke

4
สถานะสตรีมยังมีอีกมากนอกเหนือจากแฟล็ก
jww

1
ฉันหวังว่า C ++ จะอนุญาตให้ลอง / ในที่สุด นี่เป็นตัวอย่างที่ยอดเยี่ยมในการทำงานของ RAII แต่สุดท้ายก็จะง่ายกว่านี้
Trade-Ideas Philip

2
หากโครงการของคุณมีเหตุผลอย่างน้อยคุณมี Boost และมาพร้อมกับโปรแกรมรักษาสถานะเพื่อจุดประสงค์นี้
ม.ค. Hudec

9

ด้วยการปรับเปลี่ยนเล็กน้อยเพื่อให้เอาต์พุตอ่านง่ายขึ้น:

void printHex(std::ostream& x) {
   ios::fmtflags f(x.flags());
   x << std::hex << 123 << "\n";
   x.flags(f);
}

int main() {
    std::cout << 100 << "\n"; // prints 100 base 10
    printHex(std::cout);      // prints 123 in hex
    std::cout << 73 << "\n";  // problem! prints 73 in hex..
}

9

คุณสามารถสร้าง wrapper อื่นรอบ ๆ บัฟเฟอร์ stdout:

#include <iostream>
#include <iomanip>
int main() {
    int x = 76;
    std::ostream hexcout (std::cout.rdbuf());
    hexcout << std::hex;
    std::cout << x << "\n"; // still "76"
    hexcout << x << "\n";   // "4c"
}

ในฟังก์ชัน:

void print(std::ostream& os) {
    std::ostream copy (os.rdbuf());
    copy << std::hex;
    copy << 123;
}

แน่นอนว่าหากประสิทธิภาพเป็นปัญหาสิ่งนี้จะแพงกว่าเล็กน้อยเนื่องจากเป็นการคัดลอกiosออบเจ็กต์ทั้งหมด(แต่ไม่ใช่บัฟเฟอร์) รวมถึงบางสิ่งที่คุณจ่ายไป แต่ไม่น่าใช้เช่นโลแคล

ไม่เช่นนั้นฉันรู้สึกว่าถ้าคุณจะใช้.flags()มันจะดีกว่าที่จะสอดคล้องและใช้.setf()เช่นกันมากกว่า<<ไวยากรณ์ (คำถามเกี่ยวกับสไตล์ล้วนๆ)

void print(std::ostream& os) {
    std::ios::fmtflags os_flags (os.flags());
    os.setf(std::ios::hex);
    os << 123;
    os.flags(os_flags);
}

อย่างที่คนอื่นบอกคุณสามารถใส่สิ่งที่กล่าวมาข้างต้น (และ.precision()และ.fill()แต่โดยทั่วไปแล้วจะไม่ใช่ภาษาที่เกี่ยวข้องกับคำที่มักจะไม่ถูกแก้ไขและหนักกว่า) ในชั้นเรียนเพื่อความสะดวกและเพื่อให้ปลอดภัยเป็นพิเศษ std::ios&สร้างควรจะยอมรับ


จุดดี [+] แต่มันแน่นอนจำเพื่อใช้std::stringstreamสำหรับส่วนที่จัดรูปแบบเป็นมาร์ค Sherred ชี้ให้เห็น
Wolf

@ หมาป่าฉันไม่แน่ใจว่าฉันเข้าใจประเด็นของคุณ std::stringstream เป็นstd:ostreamยกเว้นใช้หนึ่งแนะนำบัฟเฟอร์กลางพิเศษ
n.caillou

แน่นอนว่าทั้งสองวิธีเป็นแนวทางที่ถูกต้องในการจัดรูปแบบเอาต์พุตทั้งแนะนำอ็อบเจ็กต์สตรีมสิ่งที่คุณอธิบายนั้นใหม่สำหรับฉัน ฉันคิดเกี่ยวกับข้อดีข้อเสียตอนนี้ อย่างไรก็ตามคำถามที่สร้างแรงบันดาลใจพร้อมคำตอบที่กระจ่างแจ้ง ... (ฉันหมายถึงตัวแปรการคัดลอกสตรีม)
Wolf

1
คุณไม่สามารถคัดลอกสตรีมได้เนื่องจากการคัดลอกบัฟเฟอร์มักไม่สมเหตุสมผล (เช่น stdout) อย่างไรก็ตามคุณสามารถมีออบเจ็กต์สตรีมหลายรายการสำหรับบัฟเฟอร์เดียวกันซึ่งเป็นสิ่งที่คำตอบนี้เสนอให้ทำ ในขณะที่std:stringstreamพินัยกรรมสร้างขึ้นเองstd:stringbuf( std::streambufอนุพันธ์) ซึ่งจะต้องเทลงในstd::cout.rdbuf()
n.caillou

ขอขอบคุณสำหรับการชี้แจง.
Wolf

0

ฉันต้องการสรุปคำตอบจาก qbert220 บ้าง:

#include <ios>

class IoStreamFlagsRestorer
{
public:
    IoStreamFlagsRestorer(std::ios_base & ioStream)
        : ioStream_(ioStream)
        , flags_(ioStream_.flags())
    {
    }

    ~IoStreamFlagsRestorer()
    {
        ioStream_.flags(flags_);
    }

private:
    std::ios_base & ioStream_;
    std::ios_base::fmtflags const flags_;
};

สิ่งนี้ควรใช้ได้กับสตรีมอินพุตและอื่น ๆ เช่นกัน

PS: ฉันต้องการแสดงความคิดเห็นนี้เป็นเพียงคำตอบข้างบน แต่ stackoverflow ไม่อนุญาตให้ทำเช่นนั้นเนื่องจากไม่มีชื่อเสียง ทำให้ฉันยุ่งกับคำตอบที่นี่แทนที่จะแสดงความคิดเห็นง่ายๆ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.