This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  ostream printf
  Submitted by



How to combine ostreams and printf style formatting
ANSI C
======
printf("%10.2f", x); 

ANSI C++ ======== cout << setw(10) << setprecision(2) << showpoint << x;

Java ==== java.text.NumberFormat formatter = java.text.NumberFormat.getNumberInstance(); formatter.setMinimumFractionDigits(2); formatter.setMaximumFractionDigits(2); String s = formatter.format(x); for (int i = s.length(); i < 10; i++) System.out.print(' '); System.out.print(s);



Well good to see us programmers have evolved :) I have to admit it, I hate using streams, the funky syntax is quite silly when you want to output simple formatted text. Printf style methods have been around for ages, but they are a dying breed - the lack of typesafety and option to add your own formatting rules is major downside. But what if we could combine the typesafety of streams and ease of formatting from using printf style format specifiers? Well here is a approach, which benefits from both worlds... ostream printf The basic idea is to create a class ostream_printf which uses the printf style format specifier string to set formatting options but outputs data in a typesafe way by using a ostream object. The syntax becomes:

ostream_printf print_obj( ostream& ref, "formatting specifiers" );
print_obj << arg0 << arg1 << ... ; 



When the ostream_printf class parses the formatting string, it does not have to deal with the problems which the printf style functions have. No more crashes due to illegal type specifiers - since the formatting string is only used to set stream formatting options like setw and setprecision.

removing the funky syntax

The solution is simply a cranked up version of the old printf macros, we use templates to automate the creation of ostream_printf object and outputting the arguments:

template <class T0, class T1
inline void oprintf( ostream& ref_, const char* fmt_, T0 t0_, T1 t1_ )
{
  ostream_print p( ref_, fmt_ );
  p << t0_ << t1_;
}

template <class T0, class T1, class T2 inline void oprintf( ostream& ref_, const char* fmt_, T0 t0_, T1 t1_, T2 t2_ ) { ostream_print p( ref_, fmt_ ); p << t0_ << t1_ << t2_; }

...



free bonus With template approach I have created two methods, oprintf and debugf, which work exactly the same but have one major diffirence:

template <class T0, class T1
inline void debugf( ostream& ref_, const char* fmt_, T0 t0_, T1 t1_ )
{
#ifndef NDEBUG
  ostream_print p( ref_, fmt_ );
  p << t0_ << t1_;
#endif
} 



...this means that you can use the "debugf" methods in timecritical areas, and simply have the calls removed in release build causing zero performance hit. This way you have a really nice way of doing common stuff like debug logs etc. And since you can make your own formatting rules the possibilties are unlimited.

runtime selection of ostream

Sometimes you would like to select if you want output data or not at runtime, and for this purpose I have created a special nullstream class derieved from std::ostream which acts like a "null driver" ostream. For instance you could use this code for retrieving statistics from you rendering class:

nullstream log;

or

std::fstream log( "render.log", std::ios::app );

oprintf( log, "blah blah" );



all done I would like to thank Kurt Miller for creating such an awsome community (Flipcode), and all the people who make this site the best on the net for us coding junkies. Please post any bugs you may find and constructive comments... The included source package includes makefiles for vc6/gcc/bcc32: ostreamprintf.zip (11k)

Refrences:
Cay Horstmann's docs on iostreams: http://www.horstmann.com
Bjarne Stroustrup "The C++ Programming language", ISBN 0-201-67504-8


Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/compiler.hpp] - (2,954 bytes)

#ifndef __COMPILER_HPP__
#define __COMPILER_HPP__

/////////////////////////////////////////////////////////////////////////////// // Autodetect compiler settings for Win32 /////////////////////////////////////////////////////////////////////////////// # if defined(_WIN32) # if defined(_MSC_VER) # define DO_MSVC 1 # define DO_WIN32 1 # define CPUSTRING "Win32-x86" # pragma warning(disable:4097) // typedef synonym for class # pragma warning(disable:4100) // unrefrenced formal parameter # pragma warning(disable:4514) // unrefrenced inline function has been removed # pragma warning(disable:4663) // changes made template specialization (c++ language) # pragma warning(disable:4710) // function could not be inlined # pragma warning(disable:4711) // function selected for automatic inline expansion # pragma warning(disable:4702) // line contains unreachable code # pragma inline_recursion( on ) // compiler should try to inline code # pragma inline_depth( 255 ) // inline recursion depth setting # elif defined(__BORLANDC__) # define DO_BCC 1 # define DO_WIN32 1 # define CPUSTRING "Win32-x86" # elif defined(__GNUC__) # define DO_GCC 1 # define DO_WIN32 1 # define CPUSTRING "Win32-x86" # elif defined(__MWERKS__) # define DO_MWERKS 1 # define DO_WIN32 1 # define CPUSTRING "Win32-x86" # elif defined(__ICL) # define DO_INTEL 1 # define DO_WIN32 1 # define CPUSTRING "Win32-x86" # endif # endif

/////////////////////////////////////////////////////////////////////////////// // Autodetect compiler settings for MacOS X /////////////////////////////////////////////////////////////////////////////// # if defined(__MACH__) && defined(__APPLE__) # ifdef __ppc__ # define DO_MACOSX 1 # define CPUSTRING "MacOSX-ppc" # elif defined __i386__ # define DO_MACOSX 1 # define CPUSTRING "MacOSX-i386" # else # define DO_MACOSX 1 # define CPUSTRING "MacOSX-other" # endif # endif

/////////////////////////////////////////////////////////////////////////////// // Autodetect compiler settings for MacOS /////////////////////////////////////////////////////////////////////////////// # if defined(__MACOS__) # define DO_MACOS 1 # define CPUSTRING "MacOS-ppc" # endif

/////////////////////////////////////////////////////////////////////////////// // Autodetect compiler settings for Linux /////////////////////////////////////////////////////////////////////////////// # if defined(__linux__) # ifdef __i386__ # define DO_LINUX 1 # define CPUSTRING "Linux-i386" # elif defined __axp__ # define DO_LINUX 1 # define CPUSTRING "Linux-alpha" # else # define DO_LINUX 1 # define CPUSTRING "Linux-other" # endif # endif

#endif

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/main.cpp] - (1,228 bytes)

#ifndef __COMMON_HPP__
#include "common.hpp"
#endif

#ifndef __OSTREAM_PRINTF_HPP__ #include "ostream_printf.hpp" #endif

#ifndef __NULLSTREAM_HPP__ #include "nullstream.hpp" #endif

using namespace std;

//------------------------------------------------------------------------ int main( int argc, char* argv[] ) { try { nullstream temp;

/* standard formatted output */ oprintf( cout, "this call remains the same both in %s builds and %s builds\n", "release", "debug" );

/* debugf allows you to force the compiler to remove the call in debug builds, causing no performance hit */ debugf( cout, "this call will be removed from %s builds\n", "release" );

/* if you pass a nullstream to oprintf, the performance hit will be quite minimal */ oprintf( temp, "this will also be removed in both %s and %s builds\n", "debug", "release" );

/* test the output function */ oprintf( cout, "[%03d] rendering speed at %3.2f msec with %d tris/second, cpu usage: %-2.5G%%\n", 15, 12.456f, 102493, 54.45456f ); } catch(exception& e) { cerr << "exception caught in main: " << e.what() << endl; } return 0; }

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/debug.hpp] - (965 bytes)

#ifndef __DEBUG_HPP__
#define __DEBUG_HPP__

#include <stdexcept> #include <cassert>

/////////////////////////////////////////////////////////////////////////////// // Design by contract macros /////////////////////////////////////////////////////////////////////////////// # if defined(NDEBUG) # define require(expr) {if(!(expr))throw std::runtime_error("precondition failed: " #expr " in " __FILE__);} # define ensure(expr) {if(!(expr))throw std::runtime_error("postcondition failed: " #expr " in " __FILE__);} # define invariant(expr) {if(!(expr))throw std::runtime_error("invariant failed: " #expr " in " __FILE__);} # define check(expr) {if(!(expr))throw std::runtime_error("assertion failed: " #expr " in " __FILE__);} # else # define require(expr) assert(expr) # define ensure(expr) assert(expr) # define invariant(expr) assert(expr) # define check(expr) assert(expr) # endif

#endif

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/common.hpp] - (418 bytes)

#ifndef __COMMON_HPP__
#define __COMMON_HPP__

typedef unsigned char byte; typedef unsigned short word; typedef unsigned long dword;

typedef signed char int8; typedef signed short int16; typedef signed int int32;

typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32;

#include "compiler.hpp" #include "debug.hpp"

#endif

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/nullstream.hpp] - (1,353 bytes)

#ifndef __NULLSTREAM_HPP__
#define __NULLSTREAM_HPP__

#include <iostream>

/////////////////////////////////////////////////////////////////////////////// // nullstream /////////////////////////////////////////////////////////////////////////////// class nullstream : public std::ostream {

class nullstreambuf : public std::streambuf { public: int out_waiting() { return 0; } int sputc( int c ) { return c; } int sputn( const char*, int n ) { return n; } };

public: nullstream(); virtual std::ostream &put( char ) { return *this; } virtual std::ostream &put( signed char ) { return *this; } virtual std::ostream &put( unsigned char ) { return *this; } virtual std::ostream &seekp( std::streampos ) { return *this; }

virtual std::ostream &seekp( std::streamoff, seek_dir ) { return *this; } virtual std::streampos tellp() { return std::streampos(0); } virtual std::ostream &write( const char*, int ) { return *this; }

virtual std::ostream &write( const signed char*, int ) { return *this; }

virtual std::ostream &write( const unsigned char*, int ) { return *this; }

};

#endif

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/ostream_printf.cpp] - (3,620 bytes)

#ifndef __COMMON_HPP__
#include "common.hpp"
#endif

#ifndef __OSTREAM_PRINTF_HPP__ #include "ostream_printf.hpp" #endif

#include <cctype> using namespace std;

//------------------------------------------------------------------------ void ostream_printf::parseFormatString() { /* too many arguments were passed */ invariant( m_format[m_pos] != '\0' );

printStaticData(); /* convert printf and iso format */ int i = m_pos;

int width = 0; int precision = 6; int flags = 0; char fill = ' '; bool alternate = false;

while (m_format[i] != 0) { i++; /* these flags can run through multiple characters */ bool more = true;

while (more) { switch(m_format[i]) { case '+': flags |= ios::showpos; break; case '-': flags |= ios::left; break; case '0': flags |= ios::internal; fill = '0'; break; case '#': alternate = true; break; default: more = false; break; } if (more) i++; }

/* width specifier */ if ( isdigit(m_format[i]) ) { width = atoi(m_format+i); do{ i++; }while( isdigit(m_format[i]) ); }

/* output precision */ if (m_format[i] == '.') { i++; precision = atoi(m_format+i); do{ i++; }while( isdigit(m_format[i]) ); }

/* type based settings */ bool format_ok = true;

switch(m_format[i]) { case 'p': break; case 's': break; case 'c': case 'C': break; case 'i': case 'u': case 'd': flags |= ios::dec; break; case 'x': flags |= ios::hex; if (alternate) flags |= ios::showbase; break; case 'X': flags |= ios::hex | ios::uppercase; if (alternate) flags |= ios::showbase; break; case 'o': flags |= ios::hex; if (alternate) flags |= ios::showbase; break; case 'f': flags |= ios::fixed; if (alternate) flags |= ios::showpoint; break; case 'e': flags |= ios::scientific; if (alternate) flags |= ios::showpoint; break; case 'E': flags |= ios::scientific | ios::uppercase; if (alternate) flags |= ios::showpoint; break; case 'g': if (alternate) flags |= ios::showpoint; break; case 'G': flags |= ios::uppercase; if (alternate) flags |= ios::showpoint; break; default: /* if we encountered an unknown type specifier, skip current format string and try parsing next one... */ format_ok = false; break; }

/* if formatting string was recognized set stream options*/ if (format_ok) { m_stream.unsetf(ios::adjustfield | ios::basefield | ios::floatfield); m_stream.setf(flags); m_stream.width(width); m_stream.precision(precision); m_stream.fill(fill); break; } } /* skip type specifier and set formatting string position */ m_pos = i+1; } //------------------------------------------------------------------------ void ostream_printf::printStaticData() { while ( m_format[m_pos] != '\0' ) { if ( m_format[m_pos] == '%' ) { if ( m_format[m_pos+1] == '%' ) m_pos++; else break; } m_stream << m_format[m_pos]; m_pos++; } }

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/nullstream.cpp] - (308 bytes)

#ifndef __COMMON_HPP__
#include "common.hpp"
#endif

#ifndef __NULLSTREAM_HPP__ #include "nullstream.hpp" #endif

using namespace std;

//------------------------------------------------------------------------ nullstream::nullstream() : ostream(NULL) { init( new nullstreambuf() ); }

Currently browsing [ostreamprintf.zip] (10,083 bytes) - [cotd/src/ostream_printf.hpp] - (11,524 bytes)

#ifndef __OSTREAM_PRINTF_HPP__
#define __OSTREAM_PRINTF_HPP__

#include <iostream> #include <string>

/////////////////////////////////////////////////////////////////////////////// // ostream_printf /////////////////////////////////////////////////////////////////////////////// class ostream_printf { public: ostream_printf(std::ostream& stream_, const char fmt_[]) : m_stream(stream_) , m_format(fmt_) , m_pos(0) { /* print out initial static text data */ printStaticData(); }

~ostream_printf() { /* print out remaining static text data */ printStaticData();

/* entire formatting string was not used, too few arguments */ invariant( m_format[m_pos] == '\0' ); }

inline ostream_printf& operator<<(bool value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(void* value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(char value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(short value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(int value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(long value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(unsigned char value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(unsigned short value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(unsigned int value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(unsigned long value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(float value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(double value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(const char* value_) { parseFormatString(); m_stream << value_; return *this; } inline ostream_printf& operator<<(const unsigned char* value_) { parseFormatString(); m_stream << value_; return *this; } protected:

/* set stream formattings options */ void parseFormatString();

/* output static text data inside formatting string */ void printStaticData();

private:

/* disable assignment operator and copy constructor */ ostream_printf( ostream_printf& temp_ ); ostream_printf& operator=( ostream_printf& temp_ );

std::ostream& m_stream; const char* m_format; int m_pos; };

/////////////////////////////////////////////////////////////////////////////// // oprintf /////////////////////////////////////////////////////////////////////////////// inline void oprintf(std::ostream& stream_, const char fmt_[]) { ostream_printf print(stream_,fmt_); } //----------------------------------------------------------------------------- template <class T0> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0) { ostream_printf print(stream_,fmt_); print << t0; } //----------------------------------------------------------------------------- template <class T0,class T1> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1) { ostream_printf print(stream_,fmt_); print << t0 << t1; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2, T3 t3,T4 t4,T5 t5) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6> inline void oprintf(std::ostream& stream_, const char fmt_[], T0 t0, T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7,T8 t8) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7,T8 t8,T9 t9) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8 << t9; } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10> inline void oprintf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7,T8 t8,T9 t9,T10 t10) { ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8 << t9 << t10; }

/////////////////////////////////////////////////////////////////////////////// // debugf /////////////////////////////////////////////////////////////////////////////// inline void debugf(std::ostream& stream_, const char fmt_[]) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); #endif } //----------------------------------------------------------------------------- template <class T0> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0; #endif } //----------------------------------------------------------------------------- template <class T0,class T1> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2, T3 t3,T4 t4,T5 t5) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6> inline void debugf(std::ostream& stream_, const char fmt_[], T0 t0, T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7,T8 t8) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7,T8 t8,T9 t9) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8 << t9; #endif } //----------------------------------------------------------------------------- template <class T0,class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10> inline void debugf(std::ostream& stream_, const char fmt_[],T0 t0,T1 t1,T2 t2,T3 t3,T4 t4,T5 t5,T6 t6,T7 t7,T8 t8,T9 t9,T10 t10) { #ifndef NDEBUG ostream_printf print(stream_,fmt_); print << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8 << t9 << t10; #endif }

#endif

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.