Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Unterschiedliche Objekte erzeugen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 < [ 2 ]
000
25.07.2007, 10:09 Uhr
Oliver
S2-Pixelgeneral


Hi,

in unserm Siedler2-Remake haben wir ein großes Netz von Klassen für die unterschiedlichen Typen. Das ganze soll jetzt gespeichert (=serialisiert) und dann wieder geladen werden. Für letzteres müsste man eine riesige Switch-Orgie schreiben, in der Art ...


C++:
switch(type)
{
case 0: return new A(x);
case 1: return new B(x);
case 2: return new C(x);
case 3: return new D(x);
...
}



Die Konstruktoren hätten alle dasselbe Interface. Gibt es da schönere Varianten?
--
Demokratie ist die Diktatur der Mehrheit.

www.siedler25.org/ ( Siedler2 - Remake )
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
25.07.2007, 10:58 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)


hmm ich glaube nicht, weil es keine Typenvariable gibt.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
25.07.2007, 11:54 Uhr
virtual
Sexiest Bit alive
(Operator)


Ja, das Factory design Pattern
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
25.07.2007, 12:20 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)



Zitat von virtual:
Ja, das Factory design Pattern

Aber auch da muss man doch letzten Endes per switch (oder ähnlichem) den entsprechenden Typen auswählen, oder nicht?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
25.07.2007, 12:36 Uhr
virtual
Sexiest Bit alive
(Operator)


Man kann es dynamich gestalten, ggf. sogar ganz ohne feste Kodierung der verschiedenen Typen. Im ersten Moment ist es ein wenig mehr aufwand, der sich aber schnell lohnt, wenn es entweder viele verschiedene Typen sind oder die Typen nicht zwingen zur Kompilezeit festlegen, oder wenn man einfach die Abhängigkeiten zwischen den Modulen lösen will.
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
25.07.2007, 12:53 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)



Zitat von virtual:
Man kann es dynamich gestalten, ggf. sogar ganz ohne feste Kodierung der verschiedenen Typen.

hast du dafür gerade ein Beispiel zur Hand?

Ich hab mir das Pattern eben mal kurz bei Wikipedia angeschaut und da wird die Factory so gelöst:

C++:
public class ImageReaderFactory {
    public static ImageReader getImageReader( InputStream is ) {
        int imageType = figureOutImageType( is );

        switch( imageType ) {
        case ImageReaderFactory.GIF:
            return new GifReader( is );
        case ImageReaderFactory.JPEG:
            return new JpegReader( is );
        // etc.
        }
    }
}


Was ja im Prinzip das Selbe wie das von Oliver ist...

Bzw. steht da noch das man das switch vermeiden könnte indem man das in einer map unterbringt. Am Ende läuft das aber doch auch wieder auf das selbe Problem hinaus denke ich mir.

Dieser Post wurde am 25.07.2007 um 12:55 Uhr von Guybrush Threepwood editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
25.07.2007, 13:35 Uhr
virtual
Sexiest Bit alive
(Operator)


Ja, ist bereits eine Factory, allerdings eben eine statische.

Was mir vorschwebt ist eine dynamische, aber ob der Aufwand lohnt, hängt vom konkreten Anwendungsfall ab.

Hier eine Skizze:

1. Alle Typen, welche "Factory-enabled" sein sollen (also über die factory erstellbar sein sollen), wandern in DLLs. Dabei kann man je nach gewünschter granularität pro typ eine eigene DLL machen oder aber auch mehrere zusammenfassen.

2. Zu jeder DLL werden nun zwei Funktionen mit C Linkage definiert:

C++:
extern "C" const char** get_known_types();
extern "C" void* instantiate(const char* type_name, parameters);



also zB:

C++:
static const char* types[] = { "A-Typ", "B-Typ" };
extern "C" const char** get_known_types() {
     return types;
}

extern "C" void* instantiate(const char* type_name, parameters) {
     if (!strcmp(type_name, "A-Typ") {
         return new ATyp(parameters);
     }
     else  (!strcmp(type_name, "B-Typ") {
         return new BTyp(parameters);
     }
     else {
         return NULL;
     }
}


Sicherlich: auch hier gibt es sowas ähnliches wie ein "switch", aber die Fallunterscheidung braucht wirklich nur die Typen zu können, die unmittelbar in diesem Modul auch definiert sind. bei dem statischen Ansatz würden alle Typen in der fallunterscheidung zu berücksichtigen sein, was nicht grade flexibel oder Wartungsfreundlich ist.

Die eigentliche faktory kann nun entweder mit einer Konfigurationsdatei gefüttert werden oder eben alle DLLs aus einem bestimmten Verzeichnis dynamisch einbinden. Wie ist jetzt mal egal, aber sie funktioniert im wesentlichen wie folgt:

1. Alle DLLs mit factory-enabled Typen ermitteln. (zB: Liste der DLLs in Textfile/XML bereitstellen, oder alle DLLs in einem bestimmten Verzeichnis durchkämmen)

2. Alle DLLs nun dynamisch dazu laden (dlopen unter Unix, LoadLibrary oder so unter Windows)

3. Für jede DLL die einstiegspunkte instantiate merken und get_known_types aufrufen. So kann eine Liste von bekannten Typen aufgebaut werden, zB für jeden Typen ein Eintrag der form:

C++:
    struct type_entry {
        native_dll_handle* dll; // Handle zur DLL
        native_funtion_handle* instantiator; // Funktionseintritt für "instantiate" methode
        const char* name; // Typ name
    }



4. Alles was die Factory sonst noch tun muß, ist dann den zu einem namen passenden type_entry ermitteln und dessen instantiator aufrufen.


Den ansatz kann man auch ohne DLLs machen. Voraussetzung ist, daß das Betriebssystem es erlaubt in einem normalen Executable eintrittspunkte zu exportieren. Dann reduziert sich der Aufwand natürlich ein wenig.
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
25.07.2007, 13:52 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)


Ja stimmt flexibler als die statische Methode ist es auf jeden Fall. Aber man kommt halt um einen gewissen Flegeaufwand nicht drum herum, denn etwas in der Art

C++:
void* CreateObject(Type, param)
{
    return new Type(param);
}


ist ja leider nicht möglich
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
25.07.2007, 17:02 Uhr
0xdeadbeef
Gott
(Operator)


Ich hatte vor ewigen Zeiten mal sowas geschrieben und in der source corner gepostet - dummerweise ist die inzwischen über den Jordan gegangen, ich muss mal kucken, ob ich das nochmal ausgegraben kriege. Ich hatte das da mit template-Methoden gelöst, a la

C++:
factory<std::string, basis_klasse>::register_type<abgeleitete_klasse>("abgeleitete_klasse");

basis_klasse *p = factor<std::string, basis_klasse>::create("abgeleitete_klasse");


...und intern halt ne std::map von strings und Instantiatoren verwaltet.

Ich schau mal nach, ob ich denk Code noch wo habe. Gebt mir ein paar Stunden Zeit :P
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
25.07.2007, 18:48 Uhr
0xdeadbeef
Gott
(Operator)


Habs gefunden. Ist ein etwas längeres Listing, also...

fku++/factory.hh

C++:
#ifndef INCLUDED_FKU___FACTORY_HH
#define INCLUDED_FKU___FACTORY_HH

#include "plain_instanciator.hh"

#include <exception>
#include <iterator>
#include <map>

namespace fku {
  template<typename key_t,
           typename value_t,
           typename compare_t = std::less<key_t>,
           template <typename> class instanciator_t = plain_instanciator>
  class factory {
  private:
    typedef instanciator_t<value_t>                instanciation_policy;

  public:
    typedef key_t                                  key_type            ;
    typedef typename instanciation_policy::type    value_type          ;
    typedef compare_t                              key_compare         ;
    typedef typename instanciation_policy::pointer pointer             ;

    class invalid_key : public std::exception {
      friend class factory;
    public:
      enum reason_type { KEY_NOT_FOUND, KEY_ALREADY_USED };

    private:
      key_type    m_key;
      reason_type m_reason;

      invalid_key(key_type const &key, reason_type reason) throw()
        : exception(), m_key(key), m_reason(reason) { }

    public:
      virtual ~invalid_key() throw() { }

      inline  key_type    const &key   () const throw() { return m_key   ; }
      inline  reason_type        reason() const throw() { return m_reason; }
      virtual char        const *what  () const throw() {
        return "factory::invalid_key";
      }
    };

  private:
    class basic_instanciator {
    public:
      virtual pointer create() const = 0;
    };

    template<typename t>
    class instanciator : public basic_instanciator {
    public:
      instanciator() throw() { }

      virtual pointer create() const {
        return instanciation_policy::template create<t>();
      }
    };

    typedef std::map<key_type,
                     basic_instanciator*,
                     key_compare> container_type;

  public:
    typedef typename container_type::const_iterator         const_iterator;
    typedef typename container_type::const_reverse_iterator const_reverse_iterator;

    inline static pointer create          (key_type const &k)         {
      return instance().create_impl(k);
    }

    template<typename reg_t>
    inline static void    register_class  (key_type const &k)         {
      instance().register_class_impl<reg_t>(k);
    }

    inline static void    unregister_class(key_type const &k) throw() {
      return instance().unregister_class_impl(k);
    }

    inline static const_iterator         find     (key_type const &k) {
      return instance().m_classes.find(k);
    }

    inline static bool                   has_class(key_type const &k) {
      return find(k) != end();
    }

    inline static const_iterator         begin    () {
      return instance().m_classes.begin ();
    }

    inline static const_iterator         end      () {
      return instance().m_classes.end   ();
    }

    inline static const_reverse_iterator rbegin   () {
      return instance().m_classes.rbegin();
    }

    inline static const_reverse_iterator rend     () {
      return instance().m_classes.rend  ();
    }

    template<typename reg_t>
    class register_type {
    public:
      register_type(key_type const &k) {
        register_class<reg_t>(k);
      }
    };

  private:
    factory() throw() { }

    ~factory() throw() {
      for(typename container_type::iterator i = m_classes.begin();
          i != m_classes.end();
          ++i)
        delete i->second;
    }

    inline static factory &instance() throw() {
      static factory my_instance;
      return my_instance;
    }

    inline static void assert_valid_type(value_type const *const) throw() { }

    inline pointer create_impl(key_type const &k) const {
      typename container_type::const_iterator i = m_classes.find(k);

      if(i == m_classes.end())
        throw invalid_key(k, invalid_key::KEY_NOT_FOUND);
      else
        return i->second->create();
    }

    template<typename reg_t>
    inline void register_class_impl(key_type const &k) {
      assert_valid_type(static_cast<reg_t const *const>(0));

      if(has_class(k))
        throw invalid_key(k, invalid_key::KEY_ALREADY_USED);
      else
        m_classes[k] = new instanciator<reg_t>();
    }

    inline void unregister_class_impl(key_type const &k) throw() {
      typename container_type::iterator i = m_classes.find(k);
      if(i != m_classes.end()) {
        delete i->second;
        m_classes.erase(i);
      }
    }

    container_type m_classes;
  };
}

#endif


fku++/plain_instanciator.hh:

C++:
#ifndef INCLUDED_FKU___PLAIN_INSTANCIATOR_HH
#define INCLUDED_FKU___PLAIN_INSTANCIATOR_HH

namespace fku {
  template<typename base_t>
  class plain_instanciator {
  public:
    typedef base_t type;
    typedef type  *pointer;
    template<typename t> static pointer create() { return new t; }
  };
}

#endif


fku++/wrap_instanciator.hh

C++:
#ifndef INCLUDED_FKU___WRAP_INSTANCIATOR_HH
#define INCLUDED_FKU___WRAP_INSTANCIATOR_HH

namespace fku {
  template<template<typename> class wrapper_t>
  class wrap_instanciator {
  public:
    template<typename base_t>
    class policy {
    public:
      typedef base_t          type;
      typedef wrapper_t<type> pointer;
      template<typename t> static pointer create() { return pointer(new t); }
    };
  };
}

#endif


Und dann zum Beispiel:

C++:
#include <fku++/factory.hh>
#include <fku++/wrap_instanciator.hh>

#include <iostream>
#include <memory>
#include <string>

class animal {
public:
  std::string message;
  animal(std::string const &d) : message(d) { }
};

class cow     : public animal {
public:
  cow    () : animal("Moo!"   ) { }
};

class chicken : public animal {
public:
  chicken() : animal("BGAAAK!") { }
};

typedef fku::factory<std::string,
                     animal,
                     std::less<std::string>,
                     fku::wrap_instanciator<std::auto_ptr>::policy> factory_t;

static factory_t::register_type<cow> reg("cow");

int main() {
  factory_t::register_class<chicken>("chicken");

  if(factory_t::has_class("cow"    )) std::cout << "Yay! Cow"     << std::endl;
  if(factory_t::has_class("chicken")) std::cout << "Yay! Chicken" << std::endl;

  std::cout << factory_t::create("cow"    )->message << std::endl;
  std::cout << factory_t::create("chicken")->message << std::endl;

  try {
    factory_t::create("foo");
  } catch (factory_t::invalid_key &e) {
    std::cout << e.what() << ' ' << e.key() << ' ' << e.reason() << std::endl;
  }

  for(factory_t::const_iterator i = factory_t::begin();
      i != factory_t::end();
      ++i)
    std::cout << i->second->create()->message;

  for(factory_t::const_reverse_iterator i = factory_t::rbegin();
      i != factory_t::rend();
      ++i)
    std::cout << i->second->create()->message;

  std::cout << std::endl
            << factory_t::find("cow")->second->create()->message << std::endl;

  factory_t::unregister_class("cow");

  if(!factory_t::has_class("cow")) std::cout << "Yay!" << std::endl;
}


--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 < [ 2 ]     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: