Code Policy Suggestion (subject: inheritance)

In order to read/write a binary format "on the fly", that can have very different binary structure depending of the version I currently used this approach:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class BinaryHandler {
  public:
    DATA data;
    bool read(...) = 0;
    bool write(...) = 0;
};

.....

class Format1 : public BinaryHandler {
  //defines how read() and write() will work in case of format1
};

class Format2 : public BinaryHandler {
  //defines how read() and write() will work in case of format2
};

Handler * choose_reader(...) {
  //function - not a member of any class
  //It will scan Format1, Format2 etc.... choosing the proper format for reading
  // a sort of thing like that: 

  //Binary Handler * myhandler = new Format1; return myhandler;
}

Handler * choose_writer(...) {
  //function - not a member of any class
  //Same logic as reader, but choose the right format writer
}

class BinaryInterface {
  public:
    DATA data; //it will receive/send infos from/to BinaryHandler * (see load and save)
    bool load(...); //uses function choose_reader and executes Handler->read(...)
    bool save(...); //uses function choose_writer and executes Handler->write(...);
};


I have to remark I am the only developer of this program (I remember: I make programs for personal fun and interest... it is not a professional work).

But.... speaking about method.... I am asking myself if it would be better if I would modify a bit the system to make it more strict (thinking about abandoning the method "all public members").

But to make it more strict I would add more level of interaction like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class BinaryBase {
  protected:
    static DATA data;
};

DATA BinaryBase::data = INITIALIZE_DATA; //static becouse program will use ONE and ONE ONLY BinaryInterface object -> so I can use static without worries

class BinaryHandler : public BinaryBase {
  protected:
    bool write(...) = 0;
    bool read(...) = 0;
    friend class BinaryFileLoader;
};

//subclasses of BinaryHandler that define single versions for write() and read()

class BinaryFileLoader { 
  private: 
    BinaryHandler * choose_reader();
    BinaryHandler * choose_writer();
  public:
    bool load(...)
    bool save(...)
    //plus a one pure virtual function choosed randomly from ones truely available in BinaryInterface, in order to force the user (myself :P) to subclass BinaryFileLoader
};

class BinaryInterface: public BinaryBase, public BinaryFileLoader {
   public:
     //defines other functions, including the "virtual" one that is virtual only to force subclassing of BinaryFileLoader
};


This second method (a bit more complex in structure), in your opinion... is it better or worst?
Last edited on
Up (waiting for an answer :P)
Use singleton exemplars, perhaps?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//////////////////////////// binary_io_helper.h ////////////////////////////////

// include guard

#include <string>
#include <iosfwd>

struct DATA { /* ... */ } ;

struct binary_io_helper
{
    virtual ~binary_io_helper() {}
    virtual bool read( const std::string& path2file, DATA& data ) = 0 ;
    virtual bool write( const std::string& path2file, const DATA& data ) const = 0 ;

    static binary_io_helper& get( int version ) ;
    static binary_io_helper& get( std::istream& stm ) ;

    protected:

        // common helper functions
        // ...

        virtual bool can_handle_version( int version ) const = 0 ;
        virtual bool can_handle_stream( std::istream& stm ) const = 0 ;

        binary_io_helper() ;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//////////////////////////// binary_io_helper.cc ////////////////////////////////

#include "binary_io_helper.h"
#include <stdexcept>

namespace
{
    // typical chain of responsibility
    constexpr std::size_t MAX_VERSIONS = 100 ;
    binary_io_helper* exemplars [MAX_VERSIONS] ;
    std::size_t next_pos = 0 ;
}

binary_io_helper::binary_io_helper()
{
    if( next_pos == MAX_VERSIONS )
        throw std::out_of_range( "max versions exceeded" ) ;
    exemplars[next_pos] = this ;
    ++next_pos ;
}

binary_io_helper& binary_io_helper::get( int version )
{
    for( std::size_t i = 0 ; i < next_pos ; ++i )
        if( exemplars[i]->can_handle_version(version) ) return *exemplars[i] ;
    throw std::domain_error( "version " + std::to_string(version) + " not supported" ) ;
}

binary_io_helper& binary_io_helper::get( std::istream& stm )
{
    for( std::size_t i = 0 ; i < next_pos ; ++i )
        if( exemplars[i]->can_handle_stream(stm) ) return *exemplars[i] ;
    throw std::domain_error( "input format not recognized" ) ;
}


Typical implementation of binary_io_helper for a particular version
Note: there is no header file, just the implementation file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//////////////////////////// binary_io_helper_v_1_0.cc ////////////////////////////////

#include "binary_io_helper.h"
#include <iostream>

namespace
{
    struct binary_io_helper_v_1_0 : binary_io_helper
    {
        binary_io_helper_v_1_0( /* ... */ ) ;

        virtual bool read( const std::string& path2file, DATA& data ) override ;
        virtual bool write( const std::string& path2file, const DATA& data ) const override ;

    protected:
        virtual bool can_handle_version( int version ) const override
        {
            return version == 0x0100 ;
        }

        virtual bool can_handle_stream( std::istream& stm ) const override
        {
            bool stream_contains_v_1_0_data = false ;
            auto pos = stm.tellg() ;
            // check contents of stream to see if it is v_1_0
            // if yes, stream_contains_v_1_0_data = true ;
            stm.seekg(pos) ;
            return stream_contains_v_1_0_data ;
        }
    };

    binary_io_helper_v_1_0 exemplar { /* ... */ } ; // instantiate the prototype

    // implementation of read, write etc.

}


Typical usage:
1
2
3
4
constexpr int version = 0x0100 ; // 1.0
Data data ;
const std::string path  = "myfile.dat" ;
binary_io_helper::get(version).read( path, data ) ;

Wow thank you very much.
I am not a true programmer, so I don't know anything about pattern designs... I searched "Singleton" in order to understand your hint and seems a cool thing. Thank for showing me.

Saying that I am afraid it is not usable in my situation, and I'd like to hear your opinion (your hint was very costructive)

I have to say I semplified my routine.
Aside from read() and write() (that executes one instance only depending of formats so singleton can be used) there is a 3rd virtual function called wText() that works differently.

It is used throughout a function (void exec_plugin_wtext(params)).... when it is called, exec_plugin_wtext executes .wText() of EVERY subclass defined. So when called you must run, in sequence, the wText() function defined in every single formats (it is long to explain why...) so I am afraid that Singleton is not suitable for this 3rd thing.

Moreover there are other 2 problems. The HANDLER virtual class (or helper class, in your example) must implement some extra parameters (in class, not in functions) that can use in some specific situations (mainly to decode version 1 that lacks infos so to allow "external infos if loaded", but not only for this reason)... but perhaps this is not a problem for usage of singleton.

Finally... the heart of my "plugin system" (I call it plugin even if it is all static) is that I use a file with a list of undefined ADD_PLUGIN(ClassName, "description") that is defined only inside specific functions (plugin reader check and plugin writer check and exec_plugin_wtext) and also in another point (extends a drop-down menu of "formats available for saving", in another part of the program) and discarded afterwards.

It seems to me that Singleton could need however this "Macro System"....

Ah.... I am forgotting.... Perhaps allocating memory for every formats possible (in my case are only 3, but extensible until 100 like you correctly said) I am afraid is a bit of vaste of memory (they should persist in memory until program is in execution). In my system (not singleton one) I allocate memory only for a Handler object when I need and destroy it afterward.... I never have 2 subclass of HANDLER active in the same time.... even exec_wtext allocate memory for one subclass, destroy the memory and than continue with the second subclass until the end of list of ADD_PLUGIN(x,y) macros.

May you suggest a way to do all those things with a Singleton style? Thank a lot again for your time.

Ps your hint of adding an extra-parameter for data in order to use reference is a very great hint (simple idea, but a very good point)
> there is a 3rd virtual function called wText() that works differently.
> It is used throughout a function (void exec_plugin_wtext(params))....
> when it is called, exec_plugin_wtext executes .wText() of EVERY subclass defined.
> So when called you must run, in sequence, the wText() function defined in every single formats

Since we already have a chain of responsibility, all that needs to be done would be to go through the chain and call wText() on each object in the chain. ie:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct binary_io_helper
{
    // ...
    virtual void wText( /* ... */ ) = 0 ;

    static  void exec_wText( /* ... */ ) ;

    // ...
};

void binary_io_helper::exec_wText( /* ... */ )
{
    for( std::size_t i = 0 ; i < next_pos ; ++i ) exemplars[i]->wText( /* ... */ ) ;
}




> Finally... the heart of my "plugin system" (I call it plugin even if it is all static) is that
> I use a file with a list of undefined ADD_PLUGIN(ClassName, "description")
> that is defined only inside specific functions (plugin reader check and plugin writer check
> and exec_plugin_wtext) and also in another point (extends a drop-down menu of
> "formats available for saving", in another part of the program) and discarded afterwards.

I haven't understood this part; could you explain it, perhaps with an example?



> Perhaps allocating memory for every formats possible (in my case are only 3,
> but extensible until 100 like you correctly said) I am afraid is a bit of vaste of memory
> (they should persist in memory until program is in execution).
> In my system (not singleton one) I allocate memory only for a Handler object
> when I need and destroy it afterward....

Unless these are really huge objects, allocating them once, and then reusing them as and when required would be cheaper overall. For instance, if each of these exemplars have a size of 100 bytes each, and there are 8 versions, we are talking about just 800 or so bytes of memory.



> I searched "Singleton" in order to understand your hint

Really, the more important hint was "exemplar". Exemplars need not be (and usually are not ) singletons; in the snippet I extended the exemplar idiom to a singleton exemplar; because I thought that that was all that was required in this case.

The Exemplar (aka Prototype) design pattern is a creational pattern; its canonical use is creation of objects.
http://sourcemaking.com/design_patterns/prototype

Last edited on
Uhm... you are right...
Reading another time your replies (and your link) I figure that I am missing something about how "exemplar" works in the code you are suggesting. The principle of Chain of responsability is clear (we loop on the Base Class pointers, any pointer will run the subclass(es) functions thank of the polymorphism, pointers are initialized) But it is not clear where and how you "create new instance" for the subclass to add into chain (with = new Subclass) as it is not clear that
binary_io_helper_v_1_0 exemplar { /* ... */ } ; // instantiate the prototype

The prototype pattern is not completely clear to me

--------------------------

> > Finally... the heart of my "plugin system" (I call it plugin even if it is all static) is that
> > I use a file with a list of undefined ADD_PLUGIN(ClassName, "description")
> > that is defined only inside specific functions (plugin reader check and plugin writer check
> > and exec_plugin_wtext) and also in another point (extends a drop-down menu of
> > "formats available for saving", in another part of the program) and discarded afterwards.

> I haven't understood this part; could you explain it, perhaps with an example?

to explain what I did I must explain the "old code" I mentioned in the firt topic - entirely public-access classes)

we have BinaryInterface class that comunicates to program
BynaryHandler class is pure virtual class to interact with the correct method to read/write/wText throughout BynaryHandler subclasses (using polymorphism).
In order to do this program uses 3 functions

BynaryHandler * select_plugin_reader() //checks for the correct reader and return pointer of the new subclass
BynaryHandler * select_plugin_writer() //same for writer
void exec_plugin_wtext() //exec wText of subclasses

aside from those functions I have 2 special "include files" called "plugin_list.h" and "plugin_list__headers.h"

1
2
3
4
5
6
7
8
// Plugin_list__headers.h
#include "Format1.h"
#include "Format2.h"
#include "Format3.h"

//here I semplified... however this file will contain where the "header of every subclass" is

//Assume we have Class Format1 : public BinaryHandler    and so on 


1
2
3
4
//Plugin_list.h  - here where I declare active plugins
ADD_PLUGIN(Format1, "v.1.0 - the old version")
ADD_PLUGIN(Format2, "v.2.0 - the newest version")
ADD_PLUGIN(Format3, "Unsupported format - only used by my program")


at this point macro ADD_PLUGIN is not defined.... becouse it will be used only in 2 cpp files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//PluginChecker.cpp - checks the right plugin...
//........

BinaryHandler * select_plugin_reader(filename) {
  #ifdef ADD_PLUGIN
  #undef ADD_PLUGIN
  #endif

  BynaryHandler * pointer = NULL;
  
  #define ADD_PLUGIN(ClassName, ClassDescription)  \
         { pointer = new ClassName;  \
            bool k = pointer->read(filename);  \
            if(k == true) return pointer; \
            else delete pointer; \
         }
   #include "Plugin_list.h" 
   #undef ADD_PLUGIN

   return NULL;
}

// I know it is a bit trivial... it will return false both if format is wrong or if reading was impossible, 
// but it is an example how the current system is 


It is more or less used in this way (the code is not exactly this one, but it is more or less the same... now I don't remember the exact code for plugin write and plugin wtext but it works in a similar manner).

and SFF_PLUGIN is used also in another part of the code. Here it would be for me complex to report c++ so I will explain a sort of meta-code.

There menu in my program that allow user to "Select Binary format to use for save files". When you click it will be showed a drop-down of the list with available formats

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//METACODE

StringList list; //list of plugins.

#ifdef ADD_PLUGIN
#undef ADD_PLUGIN
#endif

#define ADD_PLUGIN(ClassName, ClassDescription) list.add(ClassDescription)

#include "Plugin_list.h"

#undef ADD_PLUGIN


(the programs will display a brief description for all formats available... the user will choose from "v.1.0 - the old version" and the other descriptions

In this way you have only to update plugin_list.h and plugin_list__headers.h to allow the program to see all plugins available (it will automaticly update select_plugin_reader() select_plugin_writer(), exec_plugin_wtext() and "user dropdown list of available formats for writing binary") [reading instead is automated checking for actual format used]
Last edited on
> where and how you "create new instance" for the subclass to add into chain
> (with = new Subclass) as it is not clear that
> binary_io_helper_v_1_0 exemplar { /* ... */ } ; // instantiate the prototype

The prototypical instance is not created using new; it has a static storage duration.

See if this makes it clearer. I've used the same names as in your code for the plugin classes. And I've annotated the code with brief explanations of how it works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//////////////////////////// BinaryHandler.h ////////////////////////////////////////

#ifndef BINARYHANDLER_H_INCLUDED
#define BINARYHANDLER_H_INCLUDED

#include <string>
#include <list>
//#include <memory> // uncomment for std::shared_ptr<>

struct DATA {} ;

struct BinaryHandler
{
    // ---------  interface that clients will use  ----------------------------

    static BinaryHandler& get_plugin_reader_for_file( const std::string& filename ) ;
    static BinaryHandler& get_plugin_with_description( const std::string& desc ) ;
    static std::list<std::string> get_all_plugin_descriptions() ;

    virtual bool read( const std::string& filename, DATA& data ) const = 0 ;
    virtual bool write( const std::string& filename, const DATA& data ) const = 0 ;
    // get text - return a description of the plugin
    virtual const std::string& get_wtext() const ;


    // ---------  not really required for our use case  -----------------------

    // if multiple instances of plugins are really required, uncomment this
    // and override and implement in each derived class plugin
    // note: we would also need to colour the constructor; during construction,
    // these objects should be distinguishable from the prototypical object;
    // these objects should not be added to the chain of responsibilty.
    // this would then be the standard exemplar idiom in C++
    // (which is a 'smart' implemementation of the prototype pattern).

    // create a new plugin object belonging to the same class
    // virtual std::shared_ptr<BinaryHandler> clone() const = 0 ;


    // ---------  functions used internally by the plugin framework  -----------

    protected:
        virtual ~BinaryHandler() ;

        // can_read; return true if the file can be read by the plugin
        virtual bool can_read( const std::string& filename ) const = 0 ;

        const std::string description ;

        BinaryHandler( const std::string& desc ) ;
};

#endif // BINARYHANDLER_H_INCLUDED 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//////////////////////////// BinaryHandler.cc ////////////////////////////////////////

#include "BinaryHandler.h"
#include <stdexcept>

namespace
{
    // chain of responsibility
    constexpr std::size_t MAX_VERSIONS = 100 ;
    BinaryHandler* exemplars [MAX_VERSIONS] ;
    std::size_t next_pos = 0 ;
}

// the constructor of a derived class plugin would invoke this base class constructor
// each derived class implementation is responsible for instantiating a singleton
// protypical object, we trap the base class constructor invocation, and store
// a pointer to the object in our chain of responsibility.
BinaryHandler::BinaryHandler( const std::string& desc ) : description(desc)
{
    if( next_pos == MAX_VERSIONS ) throw std::out_of_range( "max versions exceeded" ) ;
    exemplars[next_pos] = this ;
    ++next_pos ;
}

// functions to select the right plugin
BinaryHandler& BinaryHandler::get_plugin_reader_for_file( const std::string& filename )
{
    // go though the chain of responsibilty, asking each plugin 'can you read this file?'
    for( std::size_t i = 0 ; i < next_pos ; ++i )
        if( exemplars[i]->can_read(filename) ) return *exemplars[i] ;
    throw std::domain_error( "unsupported file format" ) ;
}

BinaryHandler& BinaryHandler::get_plugin_with_description( const std::string& desc )
{
    // go though the chain of responsibilty, 'is this the description of the plugin?'
    for( std::size_t i = 0 ; i < next_pos ; ++i )
        if( exemplars[i]->get_wtext() == desc ) return *exemplars[i] ;
    throw std::domain_error( "no plugin with description " + desc ) ;
}

std::list<std::string> BinaryHandler::get_all_plugin_descriptions()
{
    std::list<std::string> lst ;
    for( std::size_t i = 0 ; i < next_pos ; ++i )
        lst.push_back( exemplars[i]->get_wtext() ) ;
    return lst ;
}


const std::string& BinaryHandler::get_wtext() const { return description ; }

BinaryHandler::~BinaryHandler() {}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//////////////////////////// Format1.cc ////////////////////////////////////////

// typical plugin implementation
// there is no header file for a plugin; a plugin implementation class is
// programmatically invisible to the rest of the program

// to define a plugin, we have a plugin implementation derived fron BinaryHandler
// and we instantiate one prototypical exemplar object from within our implementation

// this prototypical object is added to the chain of responsibility maintained by the
// base class, and through it, it becomes automagically available to the program

// to define a new plugin, just implement the plugin in its own file,
// and instantiate one prototypical object in the same file.

#include "BinaryHandler.h"

namespace // the implementation of the plugin is programmatically invisible to others
{
    const char* const Format1_plugin_description = "v.1.0 - the old version" ;

    struct Format1 : BinaryHandler
    {
        Format1( /* constrctor args if any */ )
          : BinaryHandler(Format1_plugin_description) // base class
        {
            // ...
        }

        bool read( const std::string& filename, DATA& data ) const override
        {
            // read the file into data, return true on success
            // ...
            return true ;
        }

        bool write( const std::string& filename, const DATA& data ) const override
        {
            // write data into the file, return true on success
            // ...
            return true ;
        }

        protected:
            // can_read; return true if the file can be read by the plugin
            virtual bool can_read( const std::string& filename ) const
            {
                // check if file is of a format that can be read. If yes,
                return true ;
                // else return false
            }
    };

    // this is where and how we "create an instance" of the subclass
    // note: this object has a static storage duration; it is not created with 'new'
    // instantiate one Format1 object; this is the prototypical exemplar object
    // the base class constructor will add this object to the chain of responsibility

    Format1 exemplar_object { /* constrctor args if any */ } ;

    // the above uses uniform initialization syntax (C++11)
    // http://www.stroustrup.com/C++11FAQ.html#uniform-init

    // or in C++98:

    // Format1 exemplar_object( /* constrctor args if any */ ) ;
    // note: you may have to take care of 'the most vexing parse' problem
    // by using extra paratheses around the constructor arguments.
    // http://en.wikipedia.org/wiki/Most_vexing_parse
}
Last edited on
Sorry for this so-late reply. I had some problems... moreover I needed to read deeply a lot of times in order to understand (thank for the big work).

Now it is a bit more cear.... Format1 exemplar_object is like a global object that is created and mantained during the runtime of application right? ...if so... I don't understand why you use { }

A thing that it is not clear to me (I forgot to ask before) is why the first lines of "chain of responsability" are closed inside a "namespace" (and outside class)... I would probably think about static protected (or private) variable.

And wow.... I never encountered (neither know) the "most vexing parse" problem... thank for pointing it (nice to learn :D)

> > Importantly, X{a} constructs the same value in every context, so that {}-initialization > > gives the same result in all places where it is legal. For example:

1
2
3
4
5
        X x{a}; 
	X* p = new X{a};
	z = X{a};         // use as cast
	f({a});           // function argument (of type X)
	return {a};       // function return value (function returning X) 

This thing is not clear for me. It is a new thing I don't know.

Thank for all your help and patience
> Format1 exemplar_object is like a global object that is created and mantained during the runtime of application right?

Yes.


> ...if so... I don't understand why you use { }

You mean here: Format1 exemplar_object { /* constrctor args if any */ } ;
For this particular example, there is a default constructor, and we could have just written:

Format1 exemplar_object ;



> A thing that it is not clear to me (I forgot to ask before) is why the first lines of "chain of responsability"
> are closed inside a "namespace" (and outside class)...
> I would probably think about static protected (or private) variable.

A static protected (or private) variable is not accessible by users; but it is still programmatically visible outside the component.

Here, it is a pure implementation detail which is of no interest to the users of BinaryHandler; and inline access to it is not required. It is better to make it programmatically invisible to the outside world; and so we place it in an anonymous namespace (internal linkage) in the implementation file.


> This thing is not clear for me.

Which is the part that is not clear?
Here are a couple of more links:
http://www.informit.com/articles/article.aspx?p=1852519
http://akrzemi1.wordpress.com/2011/06/29/brace-brace/

Ah... I understood now.... I didn't know that now (with a recent compiler) you can initialize a var or an object using braces.

This is a nice thing to know.... I usually use mingw-gcc (gcc 4.4) under windows so there should be no problems on using it (Fortunaly I never be forced to use cygwin that is still bind to gcc 3.x .... and updating it is very tricky)... Under linux no problems at all with gcc updates :D
(And no... I will never use Visual Studio... I hate to be forced to register it and than create an exe dependant of the no-sense .NET framework)

> > A thing that it is not clear to me (I forgot to ask before) is why the first lines of "chain of responsability"
> > are closed inside a "namespace" (and outside class)...
> > I would probably think about static protected (or private) variable.

> Here, it is a pure implementation detail which is of no interest to the users of
> BinaryHandler; and inline access to it is not required. It is better to make it
> programmatically invisible to the outside world; and so we place it in an anonymous
> namespace (internal linkage) in the implementation file.

This last thing is not clear for me.... how the empty namespace exactly works? (and how it will interpreted / linked?)... what is the difference from a "normal" global variable hided to the outside world (like could be, for example, Format1 myformat1)? And what it is the difference (in case if accessible only in the current source file) from a static global variable?
Last edited on
> (with a recent compiler) you can initialize a var or an object using braces.

Now this should have become clear: 'A {}-initialization gives the same result in all places where it is legal.'
A {}-initializer can even be used to initialize anonymous objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

struct A
{
   int i ;
   double d ;
   char cstr[10] ;
};

std::ostream& operator<< ( std::ostream& stm, const A& a )
{ return stm << "A{" << a.i << ',' << a.d << ',' << '"' << a.cstr << '"' << '}' ; }

A foo( const A& a ) 
{
   std::cout << "foo( " << a << " )\n" ;
   return { a.i + 12, a.d - 3.5, "hello" } ; 
}

int main()
{
    std::cout << foo( { 1, 23.4, "abc" } ) << '\n' ;
}

http://liveworkspace.org/code/W9kR0$0



> This last thing is not clear for me.... how the empty namespace exactly works?
> (and how it will interpreted / linked?)

See: http://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage-in-c



> I usually use mingw-gcc (gcc 4.4) under windows

This is a good, prebuilt MinGW distribution - with a recent compiler and boost. http://nuwen.net/mingw.html

Thank for all... and for that prebuilt MinGW with boost... even I am not used to use boost (becouse I use Qt libraries that has their own methods in order to do almost all things I need) it is very confortable to have a prebuilt one :D
Topic archived. No new replies allowed.