|
Message Box ostream
Submitted by |
I present here a msgboxstream, a std::basic_ostream-derived
class that presents its string buffer content as a Win32 MessageBox
when invoked. The title can be set through an ostream, and the style
can be controlled through good old accessors or (for the wilfully
esoteric) via insertion manipulators.
As an exercise for myself I abstracted the actual message box
mechanism out of the class and into a template policy. Under non-win32
builds the message will be displayed upon std::cout, and input
will be gathered from std::cin (only recognises 'Y' and 'y' - all
other input is false). The cout_messagebox mechanism is merely
a minimal example of how to bake your own mechanisms - I don't
expect anyone to use it.
I realised when developing this that I could leverage a std::stringstream
into being the streambuf used by the ostream, and it certainly
saved extraneous work. However, this shortcut makes the class
less informative for those wishing to learn how to hack their
own streams. To this end I've thrown into the zip file some other
streams of mine that use custom streambufs: a teestream, a debugstream
(quite common these days - took me ages to solidify when I first
hacked it), and for kicks a nilstream (I #define debugstream into
this for release builds when debugging output is no longer required).
The file main.cpp contains example usage of the classes, and even
includes a free game requiring extreme player concentration, and
an insensitivity to violence! (ahem)
The code has been compiled and executes fine on Visual Studio 6.0
with latest service packs. It _should_ compile on non-WIN32 boxes,
but I haven't tested it - please let me know if you try it.
All comments and constructive criticism will be appreciated. I just
hope that someone finds the package educational.
Regards,
A.
|
Currently browsing [msgboxstream.zip] (8,671 bytes) - [anthony/debugstream.h] - (3,619 bytes)
// ---------------------------------------------------------------------------
// $Header: /fp/fp1/67NSYY1QMG4igynohtna/anthony/debugstream.h,v 1.7 2002/06/01 15:25:32 ynohtna Exp $
//
// (c) Copyright 2002 Anthony Bowyer-Lowe. All rights reserved.
//
// Anthony Bowyer-Lowe / ynohtna
// ynohtna@ynohtna.org
// http://www.ynohtna.org/
// ---------------------------------------------------------------------------
#if !defined(ALPHA_808_DEBUGSTREAM_H) && defined(WIN32)
#define ALPHA_808_DEBUGSTREAM_H
#include <ostream>
#include <sstream>
#include <string>
#define WIN32_LEAN_AND_MEAN
#define STRICT
#include <windows.h>
// ---------------------------------------------------------------------------
// A stream that pipes its input upon a flush to the Windows debugging
// console.
// Very useful for leveraging custom class stream insertion operators into
// debug code, and for using C++ stream semantics for debugging instead of
// C-style DebugPrint(...) calls.
//
// Usage:
// anthony::debugstream dbg;
// dbg << object << " - " << object.statistic() << std::endl;
// or via an anonymous object
// anthony::debugstream() << std::complex<float>(2.3f, -0.1f);
//
// To disable debug output (in release builds for example), substitute
// the debugstream (via a macro-controlled header inclusion) with either
// an anthony::nullstream, a nilstream, an unattached anthony::teestream,
// or even a std::ofilestream connected to "/dev/null" or "NUL".
//
// Todo:
// Implement synchronisation of debugstreams in multithreaded code through
// a template policy parameter.
// ---------------------------------------------------------------------------
namespace anthony
{
#if 0
} // Nil indentum.
#endif
// Custom streambuf class for the debugstream --------------------------------
template<typename E, typename T = std::char_traits<E> >
class basic_debugbuf : public std::basic_stringbuf<E, T>
{
public:
basic_debugbuf() { }
~basic_debugbuf() {
sync();
}
protected:
int sync() {
output_debug_string(str().c_str());
str(std::basic_string<E>()); // Clear the string buffer.
return 0;
}
void output_debug_string(const E* text) {
}
};
// Specialisations for char and wchar_t to call the correct Win32 APIs -------
template<>
void basic_debugbuf<char>::output_debug_string(const char* text) {
::OutputDebugStringA(text);
}
template<>
void basic_debugbuf<wchar_t>::output_debug_string(const wchar_t* text) {
::OutputDebugStringW(text);
}
// The debugstream -----------------------------------------------------------
template<typename E, typename T = std::char_traits<E> >
class basic_debugstream : public std::basic_ostream<E, T>
{
typedef basic_debugbuf<E, T> debugbuf;
public:
// Connect the base ostream with the custom streambuf.
basic_debugstream() : std::basic_ostream<E, T>(new debugbuf())
{ }
~basic_debugstream() {
delete rdbuf();
}
};
// Typedefs for the obvious template instantions -----------------------------
typedef basic_debugstream<char> debugstream;
typedef basic_debugstream<wchar_t> debugstreamw;
// Helper functor for debugging. Useful in conjunction with std::for_each ----
template<typename T>
struct debug_outputter
{
void operator()(const T& t) {
anthony::debugstream() << t << std::endl;
}
};
// ---------------------------------------------------------------------------
} // namespace anthony
// ---------------------------------------------------------------------------
#endif // ALPHA_808_DEBUGSTREAM_H && WIN32
|
|
Currently browsing [msgboxstream.zip] (8,671 bytes) - [anthony/msgboxstream.h] - (5,229 bytes)
// ---------------------------------------------------------------------------
// $Header: /fp/fp1/67NSYY1QMG4igynohtna/anthony/msgboxstream.h,v 1.1 2002/06/01 15:25:32 ynohtna Exp $
//
// (c) Copyright 2002 Anthony Bowyer-Lowe. All rights reserved.
//
// Anthony Bowyer-Lowe / ynohtna
// ynohtna@ynohtna.org
// http://www.ynohtna.org/
// ---------------------------------------------------------------------------
#if !defined(STUFF_YOUR_MESSAGE_BOX_UP_YOUR_STREAM)
#define STUFF_YOUR_MESSAGE_BOX_UP_YOUR_STREAM
#include <iostream>
#include <ostream>
#include <sstream>
// ---------------------------------------------------------------------------
namespace anthony {
namespace msgbox {
#if 0
} // Nil auto-indentum.
}
#endif
// ---------------------------------------------------------------------------
#if defined(WIN32) // Yay, Windows!
// Win32 message box helper, specialised against char and wchar_t ------------
template<typename E>
struct w32_messagebox
{
enum { default_style = MB_OK };
int display(const E* title, const E* text, int style);
};
template<>
int w32_messagebox<char>::display(const char* title, const char* text, int style) {
return MessageBoxA(0, text, title, style);
}
template<>
int w32_messagebox<wchar_t>::display(const wchar_t* title, const wchar_t* text, int style) {
return MessageBoxW(0, text, title, style);
}
#define DEFAULT_MSGBOX_MECH w32_messagebox
#else // Yay, not Windows!
#define DEFAULT_MSGBOX_MECH cout_messagebox
#endif
// Non-Win32 implementation of a message box - no use, merely an example -----
template<typename E>
struct cout_messagebox
{
enum { default_style = 0 };
int display(const E* title, const E* text, int style) {
std::cout << '[' << title << ']' << std::endl;
std::cout << text << std::endl;
std::string input;
std::cin >> input;
return input[0] == 'y' || input[0] == 'Y';
}
};
// ---------------------------------------------------------------------------
namespace helper
{ // Manipulators for insertion into the msgboxstream to trigger operations.
struct style_adder {
int v_;
style_adder(int v) : v_(v)
{ }
};
struct display {
};
} // namespace helpers
// Note: both icon and buttons perform the same action - namely modifying
// the style property of the message box. Both are supplied to provide a
// modicum of readability (xref literate-programming) to client code. Viz:
// mb << icon(my_icon_style) << buttons(my_desired_buttons);
inline helper::style_adder icon(int i) {
return helper::style_adder(i);
}
inline helper::style_adder buttons(int i) {
return helper::style_adder(i);
}
// Allow client code to display the message box through an insertion.
// Personally I think "mb << msgbox::display();" is ugly and involves too
// much typing, but I've included this as an example of how to code different
// custom insertion operations.
inline helper::display display() {
return helper::display();
}
// The actual msgboxstream class itself --------------------------------------
template<typename E, typename T = std::char_traits<E>, typename P = DEFAULT_MSGBOX_MECH<E> >
class basic_msgboxstream : public std::basic_ostream<E, T>
{
typedef std::basic_ostream<E, T> parent; // Typedefs are A Good Thing.
typedef std::basic_streambuf<E, T> buffer;
typedef basic_msgboxstream<E, T, P> my_type;
P box_mechanism_; // Object that knows how to display our msgbox.
std::stringstream buffer_; // Don't need custom streambufs for this class.
std::stringstream title_;
int style_;
int result_;
public:
basic_msgboxstream()
: parent(buffer_.rdbuf()), box_mechanism_(P()), style_(P::default_style), result_(0)
{ }
basic_msgboxstream(P worker)
: parent(buffer_.rdbuf()), box_mechanism_(worker), style_(P::default_style), result_(0)
{ }
// Returns the stream that contains the title to be displayed for this msgbox.
std::ostream& title() {
return title_;
}
// Show
int display() {
flush(); // Ensure we've collected all needed content.
title_.flush();
result_ = box_mechanism_.display(title_.str().c_str(), buffer_.str().c_str(), style_);
return result_;
}
// Accessor and mutator for the msgbox style.
void style(int s) {
style_ = s;
}
int style() const {
return style_;
}
// Accessors for the msgbox's result code. Is zero if it hasn't been displayed.
operator int() const {
return result_;
}
int result() const {
return result_;
}
// Manipulations triggered by insertion of custom objects.
friend my_type& operator<<(my_type& mb, const helper::style_adder& sa) {
mb.style_ |= sa.v_;
return mb;
}
friend my_type& operator<<(my_type& mb, const helper::display&) {
mb.display();
return mb;
}
};
// For ease of use in 99% of the real world ----------------------------------
typedef basic_msgboxstream<char> msgboxstream;
typedef basic_msgboxstream<wchar_t> msgboxstreamw;
// ---------------------------------------------------------------------------
} // namespace msgbox
} // namespace anthony
#endif // STUFF_YOUR_MESSAGE_BOX_UP_YOUR_STREAM
|
|
Currently browsing [msgboxstream.zip] (8,671 bytes) - [anthony/nilstream.h] - (2,152 bytes)
// ---------------------------------------------------------------------------
// $Header: /fp/fp1/67NSYY1QMG4igynohtna/anthony/nilstream.h,v 1.4 2002/06/01 15:25:32 ynohtna Exp $
//
// (c) Copyright 2002 Anthony Bowyer-Lowe. All rights reserved.
//
// Anthony Bowyer-Lowe / ynohtna
// ynohtna@ynohtna.org
// http://www.ynohtna.org/
// ---------------------------------------------------------------------------
#if !defined(ANTHONY_NILSTREAM_HEADER)
#define ANTHONY_NILSTREAM_HEADER
#include <ios>
// ---------------------------------------------------------------------------
// A stream class that compiles away to nothing under an optimised build
// (assuming insertion code has no side-effects).
//
// Pollution of global and std namespace is necessary for correct
// operation and optimisation of this class.
//
// Polluting the std namespace is never encouraged, but is allowed when
// the polluting functions only interfer with the handling of custom (non-stl)
// classes.
//
// Will only compile if the replaced is used minimally: simple insertions,
// and the commonly used manipulators (ends, endl, flush).
// ---------------------------------------------------------------------------
namespace anthony
{
class nilstream
{
public:
nilstream() { }
nilstream& operator<<(nilstream& (__cdecl *Functor)(nilstream&)) {
return *this;
}
nilstream& operator<<(std::ios& (__cdecl *_Iomanip)(std::ios&)) {
return *this;
}
};
}
// ---------------------------------------------------------------------------
template<typename _O>
inline anthony::nilstream& operator<<(anthony::nilstream& stream, _O&) {
return stream;
}
// ---------------------------------------------------------------------------
namespace std { // Not particulary comprehensive.
anthony::nilstream& ends(anthony::nilstream& _O) { return _O; }
anthony::nilstream& endl(anthony::nilstream& _O) { return _O; }
anthony::nilstream& flush(anthony::nilstream& _O) { return _O; }
}
// ---------------------------------------------------------------------------
#endif // ANTHONY_NILSTREAM_HEADER
|
|
Currently browsing [msgboxstream.zip] (8,671 bytes) - [anthony/teestream.h] - (4,280 bytes)
// ---------------------------------------------------------------------------
// $Header: /fp/fp1/67NSYY1QMG4igynohtna/anthony/teestream.h,v 1.6 2002/06/01 15:25:32 ynohtna Exp $
//
// (c) Copyright 2002 Anthony Bowyer-Lowe. All rights reserved.
//
// Anthony Bowyer-Lowe / ynohtna
// ynohtna@ynohtna.org
// http://www.ynohtna.org/
// ---------------------------------------------------------------------------
#if !defined(ALPHA_808_TEESTREAM_HEADER)
#define ALPHA_808_TEESTREAM_HEADER
#include <algorithm>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
// ---------------------------------------------------------------------------
// A teestream is a stringstream that pipes its input onto zero or more
// ostreams.
// It's useful for relaying data from apis that only support a single ostream
// onto multiple streams (simultaneously). Often found loitering in the
// company of cout, cerr, logging files, and debug streams.
//
// Usage:
// anthony::teestream tee;
// tee.attach(std::cerr);
// tee.attach(logfile);
// tee.attach(new debugstream);
// tee << "Testing..." << std::endl;
// tee << std::complex(0.0f, 2.3f) << std::endl;
//
// Notes:
// The streams used by the teestream need to have a lifetime at least as
// long as the attached teestream.
//
// A stream pointer passed to a teestream will become owned by the teestream,
// and released at the teestream's time of death. This is to simplify
// the aforementioned lifetime issues via simple syntax:
// tee.attach(new consolestream);
//
// The data within the teestream is only relayed onto the attached streams
// when flushed (by inserting std::endl or std::flush, or by calling the
// flush method of the teestream).
//
// There is no capability at present to detach a stream from a teestream.
//
// A teestream without attached streams will consume all input, and thus
// function as a null_stream.
// ---------------------------------------------------------------------------
namespace anthony
{
#if 0
} // Bah fungoo to you, auto-indent.
#endif
// Custom streambuf that propagates contents to associates upon sync ---------
template<typename E, typename T = std::char_traits<E> >
class basic_teebuf : public std::basic_stringbuf<E, T>
{
typedef std::basic_ostream<E, T> stream;
public:
basic_teebuf()
{ }
~basic_teebuf() {
sync();
}
void add(stream* s) {
streams_.push_back(s);
}
protected:
typedef std::vector<stream*> stream_list;
stream_list streams_;
struct outputter {
const char* s_;
outputter(const char* s) : s_(s) { }
void operator()(stream* os) {
*os << s_;
os->flush();
}
};
int sync() {
std::for_each(streams_.begin(), streams_.end(), outputter(str().c_str()));
str(std::basic_string<E>()); // Clear the string buffer.
return 0;
}
};
// ---------------------------------------------------------------------------
template<typename E, typename T = std::char_traits<E> >
class basic_teestream : public std::basic_ostream<E, T>
{
typedef anthony::basic_teebuf<E, T> teebuf;
typedef std::basic_ostream<E, T> stream;
typedef std::vector<stream*> stream_ptrs;
stream_ptrs owned_streams_;
public:
basic_teestream() : std::basic_ostream<E, T>(new teebuf())
{ }
~basic_teestream() {
delete rdbuf();
stream_ptrs::const_iterator itr = owned_streams_.begin();
stream_ptrs::const_iterator end = owned_streams_.end();
for(; itr != end; ++itr)
delete *itr; // Destroy owned streams.
}
void attach(stream& s) {
teebuf* tb = reinterpret_cast<teebuf*>(rdbuf());
tb->add(&s);
}
void attach(stream* s) { // We claim ownership of this stream.
teebuf* tb = reinterpret_cast<teebuf*>(rdbuf());
tb->add(s);
owned_streams_.push_back(s); // You belong to me now.
}
};
// ---------------------------------------------------------------------------
typedef basic_teestream<char> teestream;
typedef basic_teestream<wchar_t> teestreamw;
// ---------------------------------------------------------------------------
} // namespace anthony
// ---------------------------------------------------------------------------
#endif // ALPHA_808_TEESTREAM_HEADER
|
|
Currently browsing [msgboxstream.zip] (8,671 bytes) - [main.cpp] - (3,718 bytes)
// ---------------------------------------------------------------------------
// $Header$
//
// (c) Copyright 2002 Anthony Bowyer-Lowe. All rights reserved.
//
// Anthony Bowyer-Lowe / ynohtna
// ynohtna@ynohtna.org
// http://www.ynohtna.org/
// ---------------------------------------------------------------------------
#pragma warning( disable : 4786 )
// Uncomment the following line to check out the cout_messagebox implementation
// on windows .
// #undef WIN32
#if defined(WIN32)
#pragma comment(linker, "/SUBSYSTEM:WINDOWS") // No console.
#pragma comment(linker, "/ENTRY:mainCRTStartup") // No WinMain bollox.
#define WIN32_LEANANDMEAN // No crapola.
#define STRICT // C++ and strong typing: it's the law.
#include <windows.h>
#else
// These hacks... Where does he get these fuggly hacks?
#define MB_OK 1
#define MB_ICONINFORMATION 2
#define MB_ICONEXCLAMATION 3
#define MB_YESNO 4
#define IDYES 1
#endif
// Flav, what time is it?
#include <time.h>
// ---------------------------------------------------------------------------
#include <anthony/msgboxstream.h>
#include <anthony/debugstream.h>
#include <anthony/nilstream.h>
#include <anthony/teestream.h>
// We gonna hurt you so bad --------------------------------------------------
class victim
{
long hp_;
public:
victim() : hp_(100)
{ }
void damage(long amount) {
hp_ -= amount;
}
operator bool() const {
return hp_ > 0;
}
long hp() const {
return hp_;
}
};
// Custom stream insertion operator for victims.
template<typename E, typename T>
std::basic_ostream<E, T>& operator<<(std::basic_ostream<E, T>& os, const victim& who)
{
os << "hp: " << who.hp();
return os;
}
// ---------------------------------------------------------------------------
void main()
{
namespace msgbox = anthony::msgbox;
unsigned long seed = time(0) ^ 31337; // Phear meh leet rng.
victim kenny;
bool loop = false;
do
{ // Create a message box.
msgbox::msgboxstream mb;
mb.style(MB_OK);
seed = (seed * 15625 + 1) & 32767;
long weight = seed & 0x7f; // [0, 127]
// Give the message box a title.
mb.title() << "Dropped " << weight << " cans of beer on Kenny's head.";
// Squash him like a bug.
kenny.damage(weight);
if(kenny)
{ // He's still standing? Must... Try... Harder.
mb << msgbox::icon(MB_ICONINFORMATION)
<< "Kenny was lucky and survived." << std::endl;
// Note: the msgboxstream is a bona-fide ostream and thus can
// be used in conjunction with 'common' custom class insertion
// operators.
mb << kenny << std::endl;
mb << std::endl;
mb << "Try harder?" << std::endl;
mb << msgbox::buttons(MB_YESNO);
}
else
{ // Squashed the scum-bag!
mb << msgbox::icon(MB_ICONEXCLAMATION)
<< "Oh my god, you killed Kenny!" << std::endl
<< std::endl
<< "You bastard!" << std::endl;
}
// Show the message box. Alternatively: mb << msgbox.display()
mb.display();
// Loop whilst Kenny is alive and the player is not bored.
loop = (kenny && IDYES == mb);
}
while(loop);
{ // Demonstrate the debugstream, teestream, and nilstream.
anthony::nilstream devnull;
devnull << "going nowhere" << std::endl;
anthony::teestream tee;
#if defined(WIN32)
tee.attach(new anthony::debugstream);
#endif
tee.attach(std::cout);
tee << "The older I grow, the more I distrust the" << std::endl
<< "familiar doctrine that age brings wisdom." << std::endl;
tee << "-- H. L. Mencken" << std::endl;
}
}
// ---------------------------------------------------------------------------
// eof
|
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|