Link to home
Start Free TrialLog in
Avatar of miis
miisFlag for Australia

asked on

How do I implement an ofstream.attach() in a C++ library that doesn't implement this function?

In the standard implementation of fstream in Linux, the std C++ library doesn't implement the attach function. I want to port a large amount of code (on HP Unix) that uses the function. How do I write an equivalent function?
Avatar of phoffric
phoffric

Could you provide the documentation on HP Unix attach function?
The attach function is not standard, but I'll assume you mean the one that takes a file descriptor, and gives access to it through a C++ ofstream.


1) You could use the boost file_descriptor_sink :

        http://www.boost.org/doc/libs/1_42_0/libs/iostreams/doc/classes/file_descriptor.html


2) When using gcc, depending on the version, there is an ofstream constructor that takes a file descriptor, or there is a streambuf constructor that takes a file descriptor (and a streambuf can be used to construct an ofstream). Check your version of gcc, to see what's available.


3) Alternatively, you could just write your own stream class that interfaces with a file descriptor (by calling the appropriate system functions).
Avatar of miis

ASKER

phoffric,

Documentation from the HP_UX fstream library:
    #include <fstream.h>

      typedef long streamoff, streampos;
      class ios {
      public:
                enum      seek_dir { beg, cur, end } ;
                enum      open_mode { in, out, ate, app, trunc, nocreate, noreplace } ;
                enum      io_state { goodbit=0, eofbit, failbit, badbit } ;
                // and lots of other stuff, see ios(3C++) ...
      } ;

      class ifstream : istream {
                          ifstream() ;
                          ~ifstream() ;
                          ifstream(const char* name, int =ios::in,
                                  int prot =filebuf::openprot) ;
                          ifstream(int fd) ;
                          ifstream(int fd, char* p, int l) ;

                void      attach(int fd) ;
                int       detach() ;
                void      close() ;
                void      open(char* name, int =ios::in,
                          int prot=filebuf::openprot) ;
                filebuf*  rdbuf() ;
                void      setbuf(char* p, int l) ;
      };

      class ofstream : ostream {
                          ofstream() ;
                          ~ofstream() ;
                          ofstream(const char* name, int =ios::out,
                                  int prot =filebuf::openprot) ;
                          ofstream(int fd) ;
                          ofstream(int fd, char* p, int l) ;

                void      attach(int fd) ;
                int       detach() ;
                void      close() ;
                void      open(char* name, int =ios::out, int prot=filebuf::openprot) ;
                filebuf*  rdbuf() ;
                void      setbuf(char* p, int l) ;
      };
==========================
Infinity08,
Although there may be a constructor in the Linux library which takes a fd argument, I need to implement this in an object that already exists. For example:
ofstream ofs;
ofs.attach(1) ;  // to attach the stream to cout.

The version of the compiler is:
gcc version 4.1.2 (SUSE Linux)

I need a sample of code which would implement the attach(fd) function. I didn't understand how I would use boost.
If you could change:
      ofstream ofs;
to
      my_ofstream ofs;
where my_ofstream is derived from ofstream, and then write the corresponding ofstream methods that you need in my_ofstream, then that might work.
Avatar of miis

ASKER

I need an example of how I would actually code an attach method.
Using fd, you will be converting to unix lower level commands. Please show a simple sample program of all the methods used by ofs. Provide a simple test driver and show results. Then we can talk about the derived class. I'll be back tomorrow to follow up. In particular, show how you define the fd (or are you just setting it to 1?). What precisely is the description of attach.

Is it like this: http://msdn.microsoft.com/en-us/library/aa277404(VS.60).aspx
Avatar of miis

ASKER

Yes, it is like the link you have to microsoft.com. I only want to implement the method for attach(1), that is, attach the stream to cout (file descriptor = 1). I have a class which is derived from ofstream (for example ABC). In my derived class I elther open a file (using the open() function of ofstream) or I attach(1) stdout, to use the same methods to send ouput to the class,e.g.:
ABC << "test" << endl;
>> I didn't understand how I would use boost.

Simply :

        boost::iostreams::file_descriptor_sink fdsink(1);
        boost::iostreams::stream<boost::iostreams::file_descriptor_sink> ofs(fdsink) ;

and since boost::iostreams::stream<boost::iostreams::file_descriptor_sink> is derived from std::ostream, you could do :

        std::ostream& os = ofs;

and use os wherever you need to.

But then, since you're only dealing with standard output anyway, you might as well just have done :

        std::ostream& os = std::cout;


>> I have a class which is derived from ofstream (for example ABC). In my derived class I elther open a file (using the open() function of ofstream) or I attach(1) stdout

If you cannot do this in an easier way, and really just need to add the attach method as defined, you'll have to change the internals of your ABC class to be able to handle both file names, and file descriptors. The best approach would be to treat both in the same way as much as possible - eg. by using an std::ostream to refer to either of the opened streams. See above for an example of how to make that work for file descriptors.


But, do consider dropping the whole file descriptor support, and just use std::cout instead. It'll make your code more portable (and you wouldn't have the problem you are currently experiencing).
I am not sure how pervasive the number of changes you need to make are. But I did find this reference online:  http://www.josuttis.com/libbook/io/outbuf2.hpp.html
    Code below taken from this link. I added test driver to handle the two cases you mentioned. Possibly the idea below will fit into your scheme. If so, then we'll work on the definition of attach.
/*
io/outbuf2.hpp  
The following code example is taken from the book
The C++ Standard Library - A Tutorial and Reference
by Nicolai M. Josuttis, Addison-Wesley, 1999
© Copyright Nicolai M. Josuttis 1999
*/
#include <iostream>
#include <streambuf>
#include <cstdio>

// for write():
#ifdef _msc_ver
# include <io.h>
#else
# include <unistd.h>
#endif

class fdoutbuf : public std::streambuf {
  protected:
    int fd;    // file descriptor
  public:
    // constructor
    fdoutbuf (int _fd) : fd(_fd) {
    }
  protected:
    // write one character
    virtual int_type overflow (int_type c) {
        if (c != EOF) {
            char z = c;
            if (write (fd, &z, 1) != 1) {
                return EOF;
            }
        }
        return c;
    }
    // write multiple characters
    virtual
    std::streamsize xsputn (const char* s,
                            std::streamsize num) {
        return write(fd,s,num);
    }
}; // END fdoutbuf

class fdostream : public std::ostream {
  protected:
    fdoutbuf buf;
  public:
    fdostream (int fd) : std::ostream(0), buf(fd) {
        rdbuf(&buf);
    }
};
///////////////////////////////

// Here is my test driver (under cygwin)

#include <sys/fcntl.h>
#include <stdio.h>
//#include <stdlib.h>

int main() {
   int fd;
   fdostream ABC(1);
   int one=1;
   ABC << "HELLO Version " << one << std::endl;

   fd = open( "test.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU );
   fdostream XYZ( fd );
   XYZ << "GOODBYE - Hello World" << std::endl;
}

Open in new window

Added attach method and default constructor.

Odd, if XYZ.attach(fd); is commented out, program behaves the same.

$ rm a.exe test.txt; g++ -ggdb  fd_ofstream.cpp ; ./a; cat test.txt
HELLO Plain Version 1
HELLO Version 1
GOODBYE ==> Hello World 1



/*
io/outbuf2.hpp  
The following code example is taken from the book
The C++ Standard Library - A Tutorial and Reference
by Nicolai M. Josuttis, Addison-Wesley, 1999
© Copyright Nicolai M. Josuttis 1999

CHANGE HISTORY:
  phoffric  Apr 29 2010   Added attach method
                          and default constructor
*/
#include <iostream>
#include <streambuf>
#include <cstdio>

// for write():
#ifdef _msc_ver
# include <io.h>
#else
# include <unistd.h>
#endif

class fdoutbuf : public std::streambuf {
  protected:
    int fd;    // file descriptor
  public:
    // constructor
    fdoutbuf (int _fd) : fd(_fd) {
    }
    void attach(int _fd) { fd = _fd; }
  protected:
    // write one character
    virtual int_type overflow (int_type c) {
        if (c != EOF) {
            char z = c;
            if (write (fd, &z, 1) != 1) {
                return EOF;
            }
        }
        return c;
    }
    // write multiple characters
    virtual
    std::streamsize xsputn (const char* s,
                            std::streamsize num) {
        return write(fd,s,num);
    }
}; // END class fdoutbuf

class fdostream : public std::ostream {
  protected:
    fdoutbuf buf;
  public:
    fdostream (int fd) : std::ostream(0), buf(fd) {
        rdbuf(&buf);
    }
    fdostream () : std::ostream(0), buf(1) {
        rdbuf(&buf);
    }
    void attach(int in_fd) {
       buf.attach(in_fd);
       rdbuf(&buf);
    }
}; // END fdostream
///////////////////////////////

// Here is my test driver (under cygwin)

#include <sys/fcntl.h>
#include <stdio.h>

int main() {
   int fd;
   int one=1;
   fdostream plainABC;
   plainABC.attach(1);
   plainABC << "HELLO Plain Version " << one << std::endl;

   fdostream ABC(1);
   ABC << "HELLO Version " << one << std::endl;

   fd = open( "test.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU );
   fdostream XYZ;
   XYZ.attach(fd);
   XYZ << "GOODBYE ==> Hello World " << one << std::endl;
}

Open in new window

>> Odd, if XYZ.attach(fd); is commented out, program behaves the same.
When I ran program in debugger, then when XYZ.attach(fd) is commented out, no data is written into output file (as originally expected). Not sure why the data is written to output file when attach is commented out and run outside of debugger.
OK, cockpit testing error on my part. When I comment out the attach, then the file is 0 bytes. Since I made the default fd 1, the results went to the console; but I was thinking that the final line was from the file when I added the cat test.txt to the command line. So, naturally, when I used the debugger, I could see that I was getting the expected results, since I was now looking at the results more carefully.
This is off topic, but just in case - attach may have the semantics of exclusivity so that a subsequent attach on the same file descriptor will return an error. If that is your semantics, then just add the logic to prevent concurrent attachments to the same file descriptor.
Avatar of miis

ASKER

phoffric, I may be obtuse but I can't get your code to work.
What I have is:

class MyClass : public fstream {
public:
    ...constructor...
   ...open function...
   ... read and write functions..
}

The mainline is:
MyClass ABC;
if (! output_to_file) {
    ABC.attach(1);
}
else {
   ABC.Open( "text" )
}
ABC << "Text to write to file or cout" << endl;

I would like to add an attach(fd) function WITHIN this class without having another class to deal with.
How do I do it?
>> I have a class which is derived from ofstream
>> class MyClass : public fstream
Didn't you want to work with a class derived from ofstream, not fstream?

>> but I can't get your code to work.
Now, if you tried
    class MyClass : public ofstream
as you originally requested (deriving from ofstream), but had trouble of any sorts, you are free to post a program showing your attempt, and then we can try to work through the problem. (If you want, you can just post a small representative program that illustrates the technical difficulties.)
Somehow I was thinking that you wanted to be able to handle any file descriptor. And that is what the earlier posted program covers. It is clearer to me now that you only want to handle the case where file descriptor = 1.
Avatar of miis

ASKER

I use fstream in MyClass as the class handles input as well as output. I'm sorry if I confused the issue. I only need an attach(fd) for cin (fd=0) and cout (fd=1). If is complicated to do for cin, I really only require the function to work for fd=1.
Did you consider what I said in http:#32342770 ? This really doesn't need to be very complicated. If you want to do this the proper way, you don't even need file descriptor support.
Avatar of miis

ASKER

infinity08,
I tried that code and it didn't work (I apparently didn't understand it). All I want is an attach(1) method in the example I presented in ID: 32352243.
>> I tried that code and it didn't work (I apparently didn't understand it).

Which code ? The boost code ? If so, you'll need the boost iostreams library for that to work.

But then again - you don't really need it, since you can just use std::cout instead of file descriptors. File descriptors are platform dependent, which is exactly why you are having the current problem. I advise to get rid of the code that depends on file descriptors, and use standard C++ only.


>> All I want is an attach(1) method in the example I presented in ID: 32352243.

Well, your code was :

>> The mainline is:
>> MyClass ABC;
>> if (! output_to_file) {
>>     ABC.attach(1);
>> }
>> else {
>>    ABC.Open( "text" )
>> }
>> ABC << "Text to write to file or cout" << endl;

I propose to turn that into something like (what follows is a very basic example) :
class Logger {
  private :
    std::ofstream _ofs;

  public :
    explicit Logger(const std::string& filename = "") : _ofs() {
        if (filename.length() > 0) _ofs.open(filename.c_str());
    }
    ~Logger() { if (_ofs.is_open()) _ofs.close(); }

    std::ostream& out() {
        if (_ofs.is_open()) return _ofs;
        return std::cout;
    }
};


Logger log(((output_to_file) ? "text" : ""));
log.out() << "Text to write to file or cout" << std::endl;

Open in new window

@miis:
Hopefully, the problem is now solved for you. Your posting of the test driver shown here really helped to clarify the problem:
=================
>> The mainline is:
>> MyClass ABC;
>> if (! output_to_file) {
>>     ABC.attach(1);
>> }
>> else {
>>    ABC.Open( "text" )
>> }
>> ABC << "Text to write to file or cout" << endl;
================

If there are any problems remaining, please post actual constructor, open, read and write methods that builds and runs correctly on HP Unix, but has problems in Linux. It is not clear in how you implemented the constructor, open, read and write methods:
================
>> class MyClass : public fstream {
>> public:
>>     ...constructor...
>>    ...open function...
>>    ... read and write functions..
>> }
================

Also, since we're dealing with I/O, providing usage of the read and write functions should be included in the test driver. For these items, you can leave out any details that are project specific and not related to I/O. Then we can try to build it on Linux. Once built, then the problem is solved.

If there is any possible relaxation of your requirements, let us know; and if there are certain constructs that must not change (due to your having to change too much code), let us know.
Avatar of miis

ASKER

Infinity08,
I can't add the boost library as I'm not allowed.
The statement I was referring to was:
        std::ostream& os = std::cout;
I don't want to use ABC.out() << ..... as it would mean changing too much code.
I want to use a ABC << "text" << endl;
and that's why I need an attach method.

phoffric,
I have attached the header and the C++ code for the FileBase_c class to which I want an attach function. In the example above the class would be:
FileBase_c ABC;

#ifndef FILE_BASE_H
#define FILE_BASE_H

#include <sys/types.h>
#include <sys/stat.h>
#include <iostream.h>
#include <fstream.h>

static const char CTL_EXT[] = ".ctl";

typedef struct stat FileStat_t;

class FileBase_c : public fstream {
public:
	FileBase_c();
	FileBase_c( const char *Name );
	// Virtual destructor just closes the file:
	virtual ~FileBase_c() {
		Close();
	}

	// Close the file:
	void Close( void ) {
		close();
	}

	// Get the file stats:
	void GetFileInfo( void );

	// Return the full file name including path:
	const char *FileName( void ) const
	{
		return ( FullFileName.Get() );
	}

	// Test if file is open:
	const bool_t IsOpen( void )
	{
		return ( rdbuf()->is_open() );
	}

	const bool_t IsIn( void ) const
	{
		return ( InMode );
	}

	// Check if the file has been modified:
	bool_t IsModified( void );

	const bool_t IsOut( void ) const
	{
		return ( ! InMode );
	}

protected:
	// Construct the full filename from a passed name:
	virtual void MakeFileName( const char *Name );

public:
	// Open the file.
	// Read Only:
	virtual void OpenIn( const char * Name );
	virtual void OpenIn( void );
	// Write only:
	virtual void OpenOut( const char * Name );
	virtual void OpenOut( void );

	// Read a line from the file. Returns FALSE on eof, otherwise TRUE.
	virtual bool_t ReadLine( char *Line, const size_t Len );

	void SetExt( const char *NewExt );
	void SetPath( const char *NewPath );

	// Set the modified time flag:
	void SetModified( void );

	// Write a line to the file:
	virtual bool_t WriteLine( const char *Line );

protected:
	String			Extension;
	// InMode is true if we are opened in Input mode, else it is false:
	bool_t			InMode;
	String			FullFileName;
	String			Path;

public:
	// The following structure is filled when we get the O/S file info
	FileStat_t		FileInfo;
	time_t			TimeLastModified;
};

#endif /* FILE_BASE_H */



/* C++ code follows: */
FileBase_c::FileBase_c()
	: fstream(),
	  TimeLastModified( 0 ),
	  InMode( TRUE )
{
	memset( &FileInfo, 0, sizeof( FileInfo ) );
	return;
}

// This constructor forms the filename and opens the file read-only:
FileBase_c::FileBase_c( const char *Name )
	: fstream(),
	  TimeLastModified( 0 ),
	  InMode( TRUE )
{
	memset( &FileInfo, 0, sizeof( FileInfo ) );
	OpenIn( Name );
	return;
}


void FileBase_c::GetFileInfo( void )
{
	if ( stat( FileName(), &FileInfo ) != 0 ) {
		memset( &FileInfo, 0, sizeof( FileInfo ) );
	}
	return;
}


// Check if the file has been modified since the last time we checked:
bool_t FileBase_c::IsModified( void )
{
	GetFileInfo();
	if ( FileInfo.st_mtime > 0 ) {
		if ( FileInfo.st_mtime > TimeLastModified ) {
			return ( TRUE );
		}
	}
	return ( FALSE );
}


// Check if the file has been modified since the last time we checked:
void FileBase_c::SetModified( void )
{
	TimeLastModified = FileInfo.st_mtime;
	return;
}


//	Construct the full file name.
//	Use the Path and Extension objects which have been set up prior
//		to this call
//	The derived class should override this function with their own
//		to nominate a particular directory if required.
void FileBase_c::MakeFileName( const char *Name )
{
	FullFileName.Truncate( 0 );
	// If a file contains an explicit path, then don't add any other:
	if ( Name[0] == '/' ||
			( Name[0] == '.' && Name[1] == '/' ) ) {
	}
	else {
		// Otherwise add the path and ensure a trailing /
		// If we don't have a path, don't add the '/'
		if ( Path.Length() > 0 ) {
			FullFileName << Path;
			if ( FullFileName[ FullFileName.Length() - 1 ] != '/' ) {
				FullFileName << '/';
			}
		}
	}
	// Add the default extension if required:
	if ( strchr( Name, '.' ) == cpNULL ) {
		FullFileName << Name << Extension;
	}
	else {
		FullFileName << Name;
	}
	return;
}


// Open the file for input after constructing the name.
void FileBase_c::OpenIn( const char *Name )
{
	MakeFileName( Name );
	OpenIn();
	return;
}

void FileBase_c::OpenIn( void )
{
	open( FullFileName.Get(), ios::in );
	InMode = TRUE;
	return;
}

// Open the file for output after constructing the name.
void FileBase_c::OpenOut( const char *Name )
{
	MakeFileName( Name );
	OpenOut();
	return;
}

void FileBase_c::OpenOut( void )
{
	open( FullFileName.Get(), ios::out );
	InMode = FALSE;
	return;
}

// Read a line from the file and return FALSE if EOF, else return TRUE.
// The data returned in Line is null terminated and does NOT have the
//	line feed character. Len is the maximum size of data returned.
bool_t FileBase_c::ReadLine( char *Line, const size_t Len )
{
	// Make sure file has been opened:
	if ( ! IsOpen() ) {
		return ( FALSE );
	}
	getline( Line, Len, '\n' );
	if ( eof() ) {
		return ( FALSE );
	}
	return ( TRUE );
}



void FileBase_c::SetExt( const char *NewExt )
{
	if ( NewExt != cpNULL ) {
		Extension = NewExt;
	}
	return;
}

void FileBase_c::SetPath( const char *NewPath )
{
	if ( NewPath != cpNULL ) {
		Path = NewPath;
	}
	return;
}


bool_t FileBase_c::WriteLine( const char *Line )
{
	if ( ! IsOpen() ) {
		return ( FALSE );
	}
	*this << Line << endl;
	if ( fail() ) {
		return ( FALSE );
	}
	return ( TRUE );
}

Open in new window

miis,
I meant to ask you... When you said that you couldn't get a post http:#32349547 to work, did you mean that when you took that program and tried to run it, there were compiler, run-time, or output errors? It is important to know what works and does not work in your environment.
Avatar of miis

ASKER

There were compiler errors. I tried to incorporate it into the FileBase_c class and was not able to resolve to compiler errors.
But does the program run stand-alone on your system with no problems?
Avatar of miis

ASKER

I didn't try that.
Avatar of miis

ASKER

I tried your program standalone and it compiled and ran.
I got "HELLO Version 1" to stdout and "GOODBYE ==> Hello World 1" to the file test.txt, I didn't get "HELLO Plain Version 1".
>> I didn't get "HELLO Plain Version 1"
On both HP and Linux?

The fact that the other two tests did output correctly means that the line
    write (fd, &z, 1)  
is being reached and is working as expected.

In fact,
   fdostream plainABC;
and
  fdostream ABC(1);
both result in fd = 1. In fact, you should be able to comment out the attach (when fd = 1) and you should still get the correct result.

So, if you go into the debugger for this stand-alone program, you should be able to set breakpoints at all the constructors and write and see that this is happening.
Avatar of miis

ASKER

I rechecked the code you wrote and discovered I had not copied it correctly. Once I had, it gave me the same output as you got - on Linux. I didn't try it on HP.
Now is there a simple way that I can incorporate the attach(fd) (for fd = 0 or 1) method in my FileBase_c class without having a secondary fdoutbuf class?
>> Now is there a simple way that I can incorporate the attach
First, I am trying to build your post. So far I have done this to reduce the number of compilation errors, I added the code shown below. If you have any suggestions on this port (based on what you did with the port to Linux) that would be useful.
//////////////// PORTING FROM HP UNIX ///////////////
typedef string String; // String class not defined
#define Length length
#define Truncate clear
#define Get c_str
// above defines are guesses for the String to string port

typedef bool bool_t;
#define cpNULL NULL
#define TRUE true
#define FALSE false
////////////////////////////////////////////////////

Open in new window

>> FileBase_c class without having a secondary fdoutbuf class?
What would be the problem with a secondary fdoutbuf class?
Could you provide a link for the String class. Also, a link for the attach function might also be useful.
Avatar of miis

ASKER

>> What would be the problem with a secondary fdoutbuf class?
I thought it would be cleaner? Your assumptions are correct. When the code was orignaly wriiten on the HP, we needed to write our own String class.
Ok, so you're porting your String class as well. The << operator means what - concatenate? Take a look at my assumptions about the code posted in http:#32382776 and let me know if they are OK or need to be modified. Thanks.

I was hoping to be able to build and run your program in order to avoid misunderstandings. If it does run here, then now that I know that the stand-alone program works as desired, then maybe we can integrate it into your class.
>> I thought it would be cleaner?
A little, but not really bad, IMO. However, if we get a solution working that would good, yes?
Avatar of miis

ASKER

The String library is based on Tom Swan's Code Secrets book:
http://openlibrary.org/b/OL1445317M/Tom_Swan's_code_secrets
I can give you a copy of the header if you wish?
The HP attach function is described here:
http://h71000.www7.hp.com/commercial/cplus/docs/ugv_stl.html
Avatar of miis

ASKER

String header attached.
Any solution that works would be fine.
// -------------------------------------------------------------------------
// FILE
//	str.h
//
// MODULE
//	string
//
// DESCRIPTION
//	The header for the String class.
//	As modified from the original by Tom Swan (Copyright 1993).
//
// NOTES
//
// MODIFICATION HISTORY
//	19-Mar-97	MI	Modifed by Martin Imber.
//
// REVISION CONTROL
//	$Author: mimber $
//	$Date: 1998/11/24 20:55:52 $
//	$Revision: 1.36 $
//	$State: Exp $
//	$Source: /devel/CVS/vtrac/include/str.h,v $
// ------------------------------------------------------------------------

#ifndef __STR_H
#define __STR_H   // Prevent multiple #includes

#ifndef ORA_PROC
#include <iostream.h>
#include <iomanip.h>
#include <string.h>
#include <strstream.h>
#endif

#include "applimit.h"
#include "apptype.h"

// The minimum size of a string:
static const int STRING_MIN_SIZE = 32;

class String {

#ifdef DEBUG
  friend void Dump(const char *msg, String &s);
#endif

  // I/O stream operators (strio.C)
  
  // Output stream operator
  friend class ostream & operator<< (ostream &os, const String &s);

  // Input stream operator
  friend class istream & operator>> (istream &is, String &s);

  // Output int operator
  friend int & operator<< (int &i, const String &s);
  

  // Operator friend functions (strfrnds.C)
  
  // Return result of comparing s1 and s2
  // 0 == (s1 == s2); < 0 == (s1 < s2); > 0 == (s1 > s2)
  friend int Compare(const String &s1, const String &s2);

  // Return concatenation of two String objects
  friend class String operator+(const String &s1, const String &s2);
  
  // Return concatenation of a integer and a String
  friend class String operator+(const short i1, const String &s2);
  friend class String operator+(const unsigned short i1, const String &s2);
  friend class String operator+(const int i1, const String &s2);
  friend class String operator+(const unsigned int i1, const String &s2);
  friend class String operator+(const long i1, const String &s2);
  friend class String operator+(const unsigned long i1, const String &s2);
  
  // Return concatenation of a double and a String
  friend class String operator+(const double f1, const String &s2);
  
  // Return concatenation of a char and a String
  friend class String operator+(const char c1, const String &s2);
  friend class String operator+(const unsigned char c1, const String &s2);
  
  // True if s1 equals s2
  friend int operator==(const String &s1, const String &s2);
  
  // True if s1 is not equal to s2
  friend int operator!=(const String &s1, const String &s2);
  
  // True if s1 is less than s2
  friend int operator<(const String &s1, const String &s2);

  // True if s1 is greater than s2
  friend int operator>(const String &s1, const String &s2);
  
  // True if s1 is less than or equal to s2
  friend int operator<=(const String &s1, const String &s2);
  
  // True if s1 is greater than or equal to s2
  friend int operator>=(const String &s1, const String &s2);

public:

  // Constructors (strctor.C)
  // ------------------------
  // Default constructor
  String();
  
  // Construct string object from a C-style string
  String( const char *cs );
  String( const unsigned char *cs );
  
  // Copy constructor
  String( const String &copy );
  
  // Construct null string of n characters
  String( const int n );
  
  // Constuct filled string of n characters
  String( const char c, const int n = 1 );

  // Constuct filled string of n characters
  String( const unsigned char c, const int n = 1 );

  // Destructor just deletes the char array:
	~String()
	{
		delete [] sp;
	}

  // Operator member functions (strops.C)
  // ------------------------------------
  int operator! () const { return !sp; }

  // Assignment operator
  String & operator=(const String &copy);
  String & operator=(const char *copy);
  
  // Concatenation operator (shorthand version)
  String & operator+=(const String &s);
  String & operator+=(const double f);
  String & operator+=(const short i);
  String & operator+=(const unsigned short i);
  String & operator+=(const int i);
  String & operator+=(const unsigned int i);
  String & operator+=(const long i);
  String & operator+=(const unsigned long i);
  String & operator+=(const char c);
  String & operator+=(const unsigned char c);

  // Concatenation operator for integers
  String operator+(const short i1);
  String operator+(const unsigned short i1);
  String operator+(const int i1);
  String operator+(const unsigned int i1);
  String operator+(const long i1);
  String operator+(const unsigned long i1);

  // Concatenation operator for doubles
  String operator+(const double f1);

  // Concatenation operator for chars
  String operator+(const char c1);
  String operator+(const unsigned char c1);

  // Efficient concatenation operators
  String & operator<<(const String &s);
  String & operator<<(const short i);
  String & operator<<(const unsigned short i);
  String & operator<<(const int i);
  String & operator<<(const unsigned int i);
  String & operator<<(const long i);
  String & operator<<(const unsigned long i);
  String & operator<<(const double f);
  String & operator<<(const char c);
  String & operator<<(const unsigned char c);
  String & operator<<(const char* cs);

  // Index operator
  const char & operator[](int index);

  // Other member functions (strmfs.C)
  // ---------------------------------
  // Delete substring
  // Return -1 if any errors detected
  // else return number of chars deleted
  int Delete(int index, int count);

  // Return substring
  // if count=0 (default), return from index to end of string
  String Copy(int index, int count=0) const;

  // Return and delete substring
  String Extract(int index, int count);
  String ExtractWord( int size );

  char *Get() const { return sp; }

  // return a substring, beginning at sp[index]
  char *Get(int index) const;

  int Length() const { return len; }

  // Return index of pattern; -1 if not found.  
  int Pos(const String &pattern) const;

  // Same as above but for literal strings
  int Pos(const char *cs) const;

  // Same as above but for single chars
  // Search from end of 
  // string if reverse != 0
  int Pos(const char c, int reverse=0) const;

  // Same as above but for case insensitive comparison
  int PosCI(const char *cs) const;

  // Set a string from a memory array with a length argument:
  String & Set( const void *Array, int Length );

  // Set the increment value of the object which determines how much
  //  to increase the storage size allocated for the string as it increases
  //  in size. Defaults to MIN_STRING_LENGTH.
  void SetIncrement( const int Inc );

  // change string to lower- or upper- case
  String & ToLower( void );
  String & ToUpper( void );

  // Trim leading and trailing characters in *this which are also in cs
  String & Trim(const char *cs);
  // Trim trailing characters in *this which are also in cs
  String & TrimEnd(const char *cs);

  // Truncate string at index
  // Return -1 if any errors detected
  // else return new string length
  int Truncate(int index);

private:
  int size;       // Size of memory block in bytes
  int len;        // Number of characters in string
  char *sp;       // Pointer to null-terminated string
  int SizeIncrement;	// Value to use to increase size of memory used.

  // Static class members (strstats.C)
  static char NullString[1];
  static char buf[MAX_NUM_STR_LENGTH];
  static ostrstream buf_strstream;

  // Return a string length that is an multiple of SizeIncrement;
  //  long, and of size at least ReqSize [normally called StringSize(len)]
  int StringSize( const int ReqSize ) const {
	  return ( ( ( ReqSize / SizeIncrement ) + 1 ) * SizeIncrement );
  }

};

#define spNULL ( (String *) 0 )

#endif  // __STR_H

Open in new window

OK, thanks for the header. No sense second guessing.
Your code now builds with a dummy main. If you could provide the desired minimal test driver for your FileBase_c class that you want to work, I will add it to main, and then try to add the attach method. If successful, I'll post your modified class.
Avatar of miis

ASKER

static FileBase_c ABC;
static char OutFile[] = 'test.txt"
bool output_to_term = true;   // or false
int main() {
   if ( output_to_term) {
      ABC.attach(1);
   }
   else {
      ABC.OpenOut( OutFile );
   }
   (ostream&)ABC << "text to write to output" << endl;
}
Builds and the ABC.OpenOut( OutFile ) part works ok. I'll take a look at the attach part.
>> I don't want to use ABC.out() << ..... as it would mean changing too much code.
>> I want to use a ABC << "text" << endl;

That is just syntax, and can be easily resolved by having the appropriate operator<<'s.
The main point of the code I posted, was to show the idea behind it, so you can adapt it to your specific needs.

This also means that you can still use the attach method if you really want, but I would recommend not to let it take a file descriptor. You could just let it take the value 1, and internally treat that as an alias for std::cout.
See //@@ for changes to your code below. I added an attach() which sets InMode, but I noticed that although it is set in the program, InMode is never used (at least not within the class). So, it effectively does nothing.

You said you preferred leaving out the additional class fdoutbuf. And since fd can only be 0 or 1, I did that using the filename length approach. I think from this code, you'll be able to make the corresponding changes to the input side.

I was using your original post form and what is below works with this form:
       ABC << "Text to write to file or cout" << endl;

But it does not work with your latest form:
>>  (ostream&)ABC << "text to write to output" << endl;

Why the last minute change? Do you have a mixture of these two forms in your program?
///////////// FileBase_c.h //////////////
#ifndef FILE_BASE_H
#define FILE_BASE_H

#include <iostream>
#include <fstream>
#include <streambuf>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

static const char CTL_EXT[] = ".ctl";

typedef struct stat FileStat_t;

//////////////// PORTING FROM HP UNIX ///////////////
typedef string String;
#define Length length
#define Truncate clear
#define Get c_str

typedef bool bool_t;
#define cpNULL NULL
#define TRUE true
#define FALSE false
////////////////////////////////////////////////////

class FileBase_c : public fstream {
public:
	FileBase_c();
	FileBase_c( const char *Name );
	// Virtual destructor just closes the file:
	virtual ~FileBase_c() {
		Close();
	}

	// Close the file:
	void Close( void ) {
		close();
	}

	// Get the file stats:
	void GetFileInfo( void );

	// Return the full file name including path:
	const char *FileName( void ) const
	{
		return ( FullFileName.Get() );
	}

	// Test if file is open:
	const bool_t IsOpen( void )
	{
		return ( rdbuf()->is_open() );
	}

	const bool_t IsIn( void ) const
	{
		return ( InMode );
	}

	// Check if the file has been modified:
	bool_t IsModified( void );

	const bool_t IsOut( void ) const
	{
		return ( ! InMode );
	}

   //@@phoffric: add attach
   // Hmm, InMode is set, but never used!
   void attach(int fd) {
      // only 0 and 1 are considered
      // you can add error handling if other
      InMode = (fd==0)? TRUE : FALSE;
   }

   //@@phoffric: add friend operator<<
   template <class T>
   friend std::ostream& operator<<(FileBase_c& fbc, const T value);

protected:
	// Construct the full filename from a passed name:
	virtual void MakeFileName( const char *Name );

public:
	// Open the file.
	// Read Only:
	virtual void OpenIn( const char * Name );
	virtual void OpenIn( void );
	// Write only:
	virtual void OpenOut( const char * Name );
	virtual void OpenOut( void );

	// Read a line from the file. Returns FALSE on eof, otherwise TRUE.
	virtual bool_t ReadLine( char *Line, const size_t Len );

	void SetExt( const char *NewExt );
	void SetPath( const char *NewPath );

	// Set the modified time flag:
	void SetModified( void );

	// Write a line to the file:
	virtual bool_t WriteLine( const char *Line );

protected:
	String			Extension;
	// InMode is true if we are opened in Input mode, else it is false:
	bool_t			InMode;
	String			FullFileName;
	String			Path;

   //@@phoffric: save ofstream
   std::ofstream ofs;

public:
	// The following structure is filled when we get the O/S file info
	FileStat_t		FileInfo;
	time_t			TimeLastModified;
};

#endif /* FILE_BASE_H */


///////////// FileBase_c.cpp //////////////
#include "FileBase_c.h"

/* C++ code follows: */
FileBase_c::FileBase_c()
: fstream(),
TimeLastModified( 0 ),
InMode( TRUE )
{
   memset( &FileInfo, 0, sizeof( FileInfo ) );
   return;
}

// This constructor forms the filename and opens the file read-only:
FileBase_c::FileBase_c( const char *Name )
: fstream(),
TimeLastModified( 0 ),
InMode( TRUE )
{
   memset( &FileInfo, 0, sizeof( FileInfo ) );
   OpenIn( Name );
   return;
}


void FileBase_c::GetFileInfo( void )
{
   if ( stat( FileName(), &FileInfo ) != 0 ) {
      memset( &FileInfo, 0, sizeof( FileInfo ) );
   }
   return;
}


// Check if the file has been modified since the last time we checked:
bool_t FileBase_c::IsModified( void )
{
   GetFileInfo();
   if ( FileInfo.st_mtime > 0 ) {
      if ( FileInfo.st_mtime > TimeLastModified ) {
         return ( TRUE );
      }
   }
   return ( FALSE );
}


// Check if the file has been modified since the last time we checked:
void FileBase_c::SetModified( void )
{
   TimeLastModified = FileInfo.st_mtime;
   return;
}


//	Construct the full file name.
//	Use the Path and Extension objects which have been set up prior
//		to this call
//	The derived class should override this function with their own
//		to nominate a particular directory if required.
void FileBase_c::MakeFileName( const char *Name )
{
   FullFileName.clear(); //Truncate( 0 );
   // If a file contains an explicit path, then don't add any other:
   if ( Name[0] == '/' ||
      ( Name[0] == '.' && Name[1] == '/' ) ) {
   }
   else {
      // Otherwise add the path and ensure a trailing /
      // If we don't have a path, don't add the '/'
      if ( Path.Length() > 0 ) {
         //			FullFileName << Path;
         FullFileName += Path;
         if ( FullFileName[ FullFileName.Length() - 1 ] != '/' ) {
            //				FullFileName << '/';
            FullFileName += '/';
         }
      }
   }
   // Add the default extension if required:
   if ( strchr( Name, '.' ) == cpNULL ) {
      //		FullFileName << Name << Extension;
      FullFileName += Name + Extension;
   }
   else {
      //		FullFileName << Name;
      FullFileName += Name;
   }
   return;
}


// Open the file for input after constructing the name.
void FileBase_c::OpenIn( const char *Name )
{
   MakeFileName( Name );
   OpenIn();
   return;
}

void FileBase_c::OpenIn( void )
{
   open( FullFileName.Get(), ios::in );
   InMode = TRUE;
   return;
}

// Open the file for output after constructing the name.
void FileBase_c::OpenOut( const char *Name )
{
   MakeFileName( Name );
   OpenOut();
   return;
}

void FileBase_c::OpenOut( void )
{  //@@phoffric: use saved ofstream
   ofs.open( FullFileName.Get(), ios::out );
   InMode = FALSE;
   return;
}

// Read a line from the file and return FALSE if EOF, else return TRUE.
// The data returned in Line is null terminated and does NOT have the
//	line feed character. Len is the maximum size of data returned.
bool_t FileBase_c::ReadLine( char *Line, const size_t Len )
{
   // Make sure file has been opened:
   if ( ! IsOpen() ) {
      return ( FALSE );
   }
   getline( Line, Len, '\n' );
   if ( eof() ) {
      return ( FALSE );
   }
   return ( TRUE );
}



void FileBase_c::SetExt( const char *NewExt )
{
   if ( NewExt != cpNULL ) {
      Extension = NewExt;
   }
   return;
}

void FileBase_c::SetPath( const char *NewPath )
{
   if ( NewPath != cpNULL ) {
      Path = NewPath;
   }
   return;
}


bool_t FileBase_c::WriteLine( const char *Line )
{
   if ( ! IsOpen() ) {
      return ( FALSE );
   }
   *this << Line << endl;
   if ( fail() ) {
      return ( FALSE );
   }
   return ( TRUE );
}

//@@phoffric: add friend operator<<
template <class T>
std::ostream& operator<<(FileBase_c& fbc, const T value) {
   if( fbc.FullFileName.length() == 0 ) {
      std::cout << value;
   }
   else {
      fbc.ofs << value;
   }
}

/////////// TEST DRIVER ///////////
static FileBase_c ABC;
static char OutFile[] = "test.txt";
bool output_to_term = false;   // or false

int main() {
   int one=1;
   const char charray[] = " charray ";
   const char *pStr = " pStr ";
   const double dbl = 4.12345678;
   short shrt = 234;

   if ( output_to_term) {
      ABC.attach(1);
   }
   else {
      ABC.OpenOut( OutFile );
   }
   ABC << "text to write to output" <<  one << '-' << dbl << charray << pStr << shrt << std::endl;
}

Open in new window

Avatar of miis

ASKER

I only used the (ostream&) cast as the compiler complained without it. I think it was because I didn't have an explicit ofstream object declared in the class? The code I have uses that cast in all cases. I won't have a chance to try out your mods until tomorrow.
OK, let us know how it turns out. Without the (ostream&) cast actually simplifies things, I think. Although you only asked for << to be defined for the literal string case in your test driver, I guessed that was just an example of what you were looking for. So, I generalized the ostream << operator to work on any type, as you can see by the modified test driver. If you have a user-defined type, you can define an ostream << operator on it to stream out what you need.
I realized that the last posted code version (unlike the previous version which dealt exclusively with file descriptors) should be portable on non-unix systems. So, I just ported it to Visual Studio 2008 Express, and found a non-portable issue that I fixed. Attached is the .h and .cpp code that should work on Linux and VS. I just fudged the endl issue that was solved in the first code post (see the overflow method in the auxiliary class) by using "\n" in the test driver.
///////////// FileBase_c.h //////////////
#ifndef FILE_BASE_H
#define FILE_BASE_H

#include <iostream>
#include <fstream>
#include <streambuf>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

static const char CTL_EXT[] = ".ctl";

typedef struct stat FileStat_t;

//////////////// PORTING FROM HP UNIX ///////////////
typedef string String;
#define Length length
#define Truncate clear
#define Get c_str

typedef bool bool_t;
#define cpNULL NULL
#define TRUE true
#define FALSE false
////////////////////////////////////////////////////

class FileBase_c : public fstream {
public:
	FileBase_c();
	FileBase_c( const char *Name );
	// Virtual destructor just closes the file:
	virtual ~FileBase_c() {
		Close();
	}

	// Close the file:
	void Close( void ) {
		close();
	}

	// Get the file stats:
	void GetFileInfo( void );

	// Return the full file name including path:
	const char *FileName( void ) const
	{
		return ( FullFileName.Get() );
	}

	// Test if file is open:
	const bool_t IsOpen( void )
	{
		return ( rdbuf()->is_open() );
	}

	const bool_t IsIn( void ) const
	{
		return ( InMode );
	}

	// Check if the file has been modified:
	bool_t IsModified( void );

	const bool_t IsOut( void ) const
	{
		return ( ! InMode );
	}

   //@@phoffric: add attach
   // Hmm, InMode is set, but never used!
   void attach(int fd) {
      // only 0 and 1 are considered
      // you can add error handling if other
      InMode = (fd==0)? TRUE : FALSE;
   }

   //@@phoffric: add friend operator<<
   template <class T>
   friend FileBase_c& operator<<(FileBase_c& fbc, const T value);

protected:
	// Construct the full filename from a passed name:
	virtual void MakeFileName( const char *Name );

public:
	// Open the file.
	// Read Only:
	virtual void OpenIn( const char * Name );
	virtual void OpenIn( void );
	// Write only:
	virtual void OpenOut( const char * Name );
	virtual void OpenOut( void );

	// Read a line from the file. Returns FALSE on eof, otherwise TRUE.
	virtual bool_t ReadLine( char *Line, const size_t Len );

	void SetExt( const char *NewExt );
	void SetPath( const char *NewPath );

	// Set the modified time flag:
	void SetModified( void );

	// Write a line to the file:
	virtual bool_t WriteLine( const char *Line );

protected:
	String			Extension;
	// InMode is true if we are opened in Input mode, else it is false:
	bool_t			InMode;
	String			FullFileName;
	String			Path;

   //@@phoffric: save ofstream
   std::ofstream ofs;

public:
	// The following structure is filled when we get the O/S file info
	FileStat_t		FileInfo;
	time_t			TimeLastModified;
};

#endif /* FILE_BASE_H */
///////////// FileBase_c.cpp //////////////
#include <string>
#include "FileBase_c.h"

/* C++ code follows: */
FileBase_c::FileBase_c()
: fstream(),
TimeLastModified( 0 ),
InMode( TRUE )
{
   memset( &FileInfo, 0, sizeof( FileInfo ) );
   return;
}

// This constructor forms the filename and opens the file read-only:
FileBase_c::FileBase_c( const char *Name )
: fstream(),
TimeLastModified( 0 ),
InMode( TRUE )
{
   memset( &FileInfo, 0, sizeof( FileInfo ) );
   OpenIn( Name );
   return;
}


void FileBase_c::GetFileInfo( void )
{
   if ( stat( FileName(), &FileInfo ) != 0 ) {
      memset( &FileInfo, 0, sizeof( FileInfo ) );
   }
   return;
}


// Check if the file has been modified since the last time we checked:
bool_t FileBase_c::IsModified( void )
{
   GetFileInfo();
   if ( FileInfo.st_mtime > 0 ) {
      if ( FileInfo.st_mtime > TimeLastModified ) {
         return ( TRUE );
      }
   }
   return ( FALSE );
}


// Check if the file has been modified since the last time we checked:
void FileBase_c::SetModified( void )
{
   TimeLastModified = FileInfo.st_mtime;
   return;
}


//	Construct the full file name.
//	Use the Path and Extension objects which have been set up prior
//		to this call
//	The derived class should override this function with their own
//		to nominate a particular directory if required.
void FileBase_c::MakeFileName( const char *Name )
{
   FullFileName.clear(); //Truncate( 0 );
   // If a file contains an explicit path, then don't add any other:
   if ( Name[0] == '/' ||
      ( Name[0] == '.' && Name[1] == '/' ) ) {
   }
   else {
      // Otherwise add the path and ensure a trailing /
      // If we don't have a path, don't add the '/'
      if ( Path.Length() > 0 ) {
         //			FullFileName << Path;
         FullFileName += Path;
         if ( FullFileName[ FullFileName.Length() - 1 ] != '/' ) {
            //				FullFileName << '/';
            FullFileName += '/';
         }
      }
   }
   // Add the default extension if required:
   if ( strchr( Name, '.' ) == cpNULL ) {
      //		FullFileName << Name << Extension;
      FullFileName += Name + Extension;
   }
   else {
      //		FullFileName << Name;
      FullFileName += Name;
   }
   return;
}


// Open the file for input after constructing the name.
void FileBase_c::OpenIn( const char *Name )
{
   MakeFileName( Name );
   OpenIn();
   return;
}

void FileBase_c::OpenIn( void )
{
   open( FullFileName.Get(), ios::in );
   InMode = TRUE;
   return;
}

// Open the file for output after constructing the name.
void FileBase_c::OpenOut( const char *Name )
{
   MakeFileName( Name );
   OpenOut();
   return;
}

void FileBase_c::OpenOut( void )
{  //@@phoffric: use saved ofstream
   ofs.open( FullFileName.Get(), ios::out );
   InMode = FALSE;
   return;
}

// Read a line from the file and return FALSE if EOF, else return TRUE.
// The data returned in Line is null terminated and does NOT have the
//	line feed character. Len is the maximum size of data returned.
bool_t FileBase_c::ReadLine( char *Line, const size_t Len )
{
   // Make sure file has been opened:
   if ( ! IsOpen() ) {
      return ( FALSE );
   }
   getline( Line, Len, '\n' );
   if ( eof() ) {
      return ( FALSE );
   }
   return ( TRUE );
}



void FileBase_c::SetExt( const char *NewExt )
{
   if ( NewExt != cpNULL ) {
      Extension = NewExt;
   }
   return;
}

void FileBase_c::SetPath( const char *NewPath )
{
   if ( NewPath != cpNULL ) {
      Path = NewPath;
   }
   return;
}


bool_t FileBase_c::WriteLine( const char *Line )
{
   if ( ! IsOpen() ) {
      return ( FALSE );
   }
   *this << Line << endl;
   if ( fail() ) {
      return ( FALSE );
   }
   return ( TRUE );
}

//@@phoffric: add friend operator<<
template <class T>
FileBase_c& operator<<(FileBase_c& fbc, const T value) {
   if( fbc.FullFileName.length() == 0 ) {
      std::cout << value;
   }
   else {
      fbc.ofs << value;
   }
   return fbc;
}

/////////// TEST DRIVER ///////////
static FileBase_c ABC;
static char OutFile[] = "test.txt";
bool output_to_term = false;   // or false

#define endl "\n"
int main() {
   int one=1;
   const char charray[] = " charray ";
   const char *pStr = " pStr ";
   const double dbl = 4.12345678;
   short shrt = 234;

   if ( output_to_term) {
      ABC.attach(1);
   }
   else {
      ABC.OpenOut( OutFile );
   }
   ABC << "text to write to output" <<  one << '-' << dbl << charray << pStr << shrt << endl << endl;
   ABC << "More stuff: " <<  one << '-' << dbl << charray << pStr << shrt << endl;
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of miis

ASKER

phoffric,
The sample code compiles and works on my Linux system.
However, when I try it in a program that was ported from the HP (including your mods), it gives me a compiler error of:
undefined reference to 'FileBase_c& operator<< <char>(FileBase_c&, char)'
which makes it appear that the template is not working??

Also, the (ostream&) cast doesn't work (as you said) because the attach is not referring the  underlying fstream object but to a separate ofs object. In the WriteLine() method, the use of *this would not work either I think but could be replaced with ofs. I can live with removing the (ostream&) cast if you can just fix the above compiler issue.
Sorry to hear that you have more problems. I just tried to get your test driver to work with your posted class, and did not test every function. If you could post a program with a driver that illustrates your problems so that I can see the  "undefined reference" error myself, I might be able to help further. There are potentially several other gotchas that may need to be resolved. I resolved one of them - the endl manipulator.
Avatar of miis

ASKER

I solved the problem. I had the template in the code not the header. It now works well. I assume I can do the same thing in the input side as well?
Glad you solved the problem! Yes, in general, I like the idea of putting the template definition in the header rather than in a .cpp and then include the .cpp in the .h file. But that is just a style thing. The important thing, as you may know, is that the template body must be seen by the code that uses it so that the compiler can generate the proper code.

Input has some rough edges I think. Using the analogous approach there may be sync issues (between input and output) to deal with, and more. When you said that it was OK to only deal with the output side, I figured that you have a workaround for the input side. Give it a try, and let's see if your workaround works.
Avatar of miis

ASKER

A great deal of time and efflort was spent by phoffric to answer my question and I thank you very much.
I think I/O in C++ is a bit complicated (certainly more complicated IMO than standard ANSI C). But it is more general. So, I would not be surprised if you run into more issues. I actually was going to continue with the auxiliary class approach shown in http:#32349547 (modified from the referenced book). Then, I would have modified your Open method to use the Linux open, and just work with file descriptors at that point. I am guessing that both input and output would run more smoothly. If there are other issues when you port your entire project, let us not forget that approach. If you have further problems, do not hesitate to ask. I consider this to be a first step.

BTW - if you include the C++ Programming Language zone, there are more experts who may be able to respond in more timely manner. Also, by including this zone, your final solution will be potentially more portable - recall that when I ported a working cygwin solution to VS 2008, I found some problems which I then ported back to cygwin seamlessly.
Avatar of miis

ASKER

I know I already closed this thread but I seem to have a problem with endl function.
It works ok in the example you presented but if does not work in my code.
The statement:
cout << ",,,,," << endl;
works ok but the statement
ABC << "....." << endl;
does not.
Any ideas?
Does any endl word?
ABC << "....." << endl; works OK in the posted program, yes?
If you post a program that illustrates the problem, then we can look at it (probably tomorrow).
For std::endl and similar manipulators, you need to overload the operator<< that takes an std::ostream& (*)(std::ostream&), ie.

        std::ostream& ABC::operator<<(std::ostream& (*)(std::ostream&));

Although, I still don't understand why this needs to be made so complicated ...
Avatar of miis

ASKER

Is the part of the ABC class? What goes in the header and what goes in the code?
>> Is the part of the ABC class?

That's why I used the ABC:: namespace qualifier ;)


>> What goes in the header and what goes in the code?

What usually goes there ... prototype in the header, implementation in the .cpp file (unless it's inline and/or templated).
Avatar of miis

ASKER

Infinity08:
I'm afraid that I don't understand the syntax of the code:
    std::ostream& ABC::operator<<(std::ostream& (*)(std::ostream&));

I can understand that there needs to be a friend class for ABC for the operator<< for a std::ostream& object but I don't know how to code it. Is it something like:
   friend ABC& operator<<( ABC& A, std:ostream& B);
??
Avatar of miis

ASKER

phoffric,
I am having trouble with generating a simple example of how the endl is not working with FileBase_c. The template you coded doesn't appear to work with std:ostream& and I wonder whether an explicit friend for the operator<< would work here - but I don't know how to code it.
>> I am having trouble with generating a simple example of how the endl is not working with FileBase_c.
Does this mean that when you have a simple example, then endl does work? If so, then we are both witnessing a phenomenon known as the Heisenbug Principle:
     http://en.wikipedia.org/wiki/Unusual_software_bug#Heisenbug

If so, then perhaps in your full program, there is an interaction that we are unaware of.

But, it sounds like Infinity08 has a simpler solution for you. I would create another question in the C++ Programming Language zone (where there may be more C++ contributors) in addition to the zones that you have (you are permitted 3 zones), and that way Infinity08 and other C++ experts will be able to help you refine this port. A large port like this may need several refinements.
Are you able to post a more complex example where endl fails?
It appears that you may have accepted a solution before completing your testing. In that case, you may request that this question be re-opened (or deleted) to allow this thread to be continued, or another question to be asked.

BTW - on reflection your problem is not an example of the Heisenbug Principle since you are always able to reproduce the problem.
>>     std::ostream& ABC::operator<<(std::ostream& (*)(std::ostream&));

(std::ostream& (*)(std::ostream&)) is a pointer to a function that takes a reference to a std::ostream as argument, and returns a reference to an std::ostream.
That is exactly the type of std::endl.

If you want your streaming class to support std::endl, simply provide an operator<< that takes the right type.


>> I can understand that there needs to be a friend class for ABC for the operator<< for a std::ostream& object

Do you want to stream a stream ? That rarely makes sense, and I'd say it doesn't make sense here ;)
What you need is a stream operator that takes a type that is compatible with std::endl (see above).

You don't have to make it a member operator - you can make it a friend operator too. The principle is the same.
Avatar of miis

ASKER

An example it not working.
The output should be:
test 1
 test 2
test3
BUT it is:
test 1 test 2
test3

// header 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <iostream> 
#include <streambuf> 
#include <fstream> 
#include <cstdio> 
#include <unistd.h> 

#define bool_t bool 
using namespace std; 

class FileBase_c : public std::fstream { 

public: 
        FileBase_c(); 
        // Virtual destructor just closes the file: 
        virtual ~FileBase_c() { 
                Close(); 
        } 

        // Add friend class for << operations. 
        template <class T> 
        friend FileBase_c& operator<<( FileBase_c& fbc, const T value ); 

        // Close the file: 
        void Close( void ) { 
                close(); 
        } 

        void attach( const int in_fd ); 
        void detach( void ); 
public: 

protected: 
        bool_t StdMode; 
        std::ofstream   ofs; 
}; 

template <class T> 
FileBase_c& operator<<( FileBase_c& fbc, const T value ) { 
        if ( fbc.StdMode ) { 
                std::cout << value; 
        } 
        else { 
                fbc.ofs << value; 
        } 
        return fbc; 
} 
std::ostream& endl(std::ostream& strm ); 
//class 

FileBase_c::FileBase_c() 
        :  StdMode( true ) 
{ 
        return; 
} 

void FileBase_c::attach( const int in_fd ) 
{ 
        ; 
} 

void FileBase_c::detach( void ) 
{ 
        ; 
} 
std::ostream& endl( std::ostream& strm ) { 
        strm.put( '\n' ); 
        strm.flush(); 
        return strm; 
} 




int main() 
{ 
        FileBase_c ABC; 
        ABC.attach(1); 
        ABC << "test " << 1 << endl; 
        cout << " test " << 2 << endl; 
        cout << "test3" << endl; 
        return 0; 
}

Open in new window

>> An example it not working.
In code post http:#32508767 notice that I left out
   using namespace std;

So, if you remove it, and then fix build errors by using:
   std::cout
then let me know what you get.
Avatar of miis

ASKER

That would mean I would have to change hundreds of lines of code in many programs which all reference cout and not std::cout. Can you not make it work with "using namespace std"?
In your related question, I noticed two important differences in your test driver. One is that you are requiring:
    (ostream&)of << "test " << 1 << setw(4) << 1.2 << endl;
whereas here it is just
    of << ...

and here you require a mixture of cout stream and the ABC stream, but in the related post, you don't require this mixture.

Since this is all for one port, shouldn't the two requirements be the same in terms of casting and mixtures?
Avatar of miis

ASKER

I simplified this question thinking that it would be implemented like the real attach(fd) on HP and that the io manipulators would be taken care of automatically.
The (ostream&) cast is required in the HP implementation. If it is not required I can leave it out.
What about the other issue of intermixing cout stream with your class output stream - if that is required, then for completeness, shouldn't that be added to the related thread?

Also, what latitude is there as far as deriving from fstream? I know you said you did that to allow both input and output. And is there an intermixing of cin and your class input stream? From what I understand, there are sometimes problems even with intermixing cin and other input methods.

(BTW - I will be leaving shortly, and I may take off tomorrow.)
Avatar of miis

ASKER

The FileBase_c class is primarily for output but is used occasionally for input. The attach(fd) function under HP_UX solves all that and I didn't know what I was getting into with it not being included in the C++ libraries under Linux.

I just want the example I posted in the new thread to work for output. I can live without it for input. As for deriving from fstream, I don't care as long as the example can be made to work for cout and a file.

I appreciate you following up this discussion with me.
Hi miis,
In the related thread, I see that the solution works. Is all good? Did the solution help you with how to make the attach?
Avatar of miis

ASKER

Yes. By including object of ofstream and &ostream, I could adapt the solution to work for me. Thanks again.
OK, great. When we ported a couple hundred 1000 LOC program from SCO 3 to SCO 5, it took several of us months; and that was just a port to an upgraded OS on the same PC! We finally had to purchase Parasoft Ensure++ to catch all the run time errors that were due to erroneous code (but working) in SCO 3.