2016-07-06 12 views
2

Hallo, ich versuche, ein einfaches ORM in C++ für ein Projekt zu erstellen. In diesem Beispiel unter der Annahme einer einfachen Klasse wieZugriff auf eine Liste von Feldern und Typen in einer Klasse in C++

class userProfile: public BaseOrm 
{ 
    public: 
     string username; 
     string email; 
}; 

Jetzt hat Basis-ORM eine Methode save() und migrate(). Was ich will ist, wenn eine Person migrate() all das Schema aufruft, in diesem Fall werden Benutzername und E-Mail als db -Tabellen aufgefüllt und beim Speichern bleiben sie in der Datenbank erhalten.

Was ich habe Problem mit wie bekomme ich, was alle Felder in der Klasse definiert sind, wie in diesem Beispiel username und email und auch dort Typen, string in diesem Fall. Jede Hilfe wäre willkommen.

Ich weiß, dass es in C++ keine Spiegelung gibt, deshalb interessiere ich mich nicht wirklich für den Variablennamen, sondern mehr für die Anzahl der Variablen und deren Typen, um sie mit DB abzubilden.

+0

Sie 'userProfile' als Template-Parameter auf' BaseOrm' passieren kann. –

+2

Sie sollten die Funktionen 'save' und' migrate' in Ihrer benutzerdefinierten Klasse überladen/überschreiben und an geeigneter Stelle die Basisversion der jeweiligen Funktion aufrufen. – Arunmu

+0

@ πάνταῥεῖ wenn Sie nichts dagegen haben, können Sie mich bitte auf ein Beispiel verweisen :) –

Antwort

0

Was ich verstehe, ist, dass Sie ein generisches Verhalten für Klassen wollen, die eine haben variabler Satz von Feldern.

Ich empfehle Ihnen, eine "Feld" -Schnittstelle zu erstellen, die in Ihrer Basisklasse mit einem Container gespeichert wird (zum Beispiel eine Karte von [fieldName, fieldInterface]). Sie müssen immer noch ein Verhalten für jeden Feldtyp implementieren, aber dann können Sie jede Klasse erstellen, die von der Basisklasse abgeleitet ist und eine dynamische Feldgruppe besitzt. Hier

ein Beispiel:

#include <iostream> 
#include <map> 

using namespace std; 

//the "Field" interface 
class IFieldOrm 
{ 
public: 
    virtual ~IFieldOrm() {} 

    virtual void save() = 0; 
    virtual void migrate() = 0; 
}; 

//your base class 
class BaseOrm 
{ 
public: 
    virtual ~BaseOrm(); 

    virtual void save(); 
    virtual void migrate(); 

protected: 
    map<string, IFieldOrm*> m_fields; //prefer a smart pointer if you don't want to mess with raw pointer 
}; 

//base class implementation 
void BaseOrm::save() 
{ 
    for(auto& f : m_fields) 
     f.second->save(); 
} 

void BaseOrm::migrate() 
{ 
    for(auto& f : m_fields) 
     f.second->migrate(); 
} 

//don't forget to free your "fields" pointers if you have raw pointers 
BaseOrm::~BaseOrm() 
{ 
    for(auto& f : m_fields) 
     delete f.second; 
} 


//then implement your basic types 
//(like string, int, ..., whatever type you want to store in your database) 
class StringFieldOrm : public IFieldOrm 
{ 
public: 
    StringFieldOrm(const string& value) : m_value(value) {} 

    virtual void save(); 
    virtual void migrate(); 

private: 
    string m_value; 
}; 

void StringFieldOrm::save() 
{ 
    cout << "Save value " << m_value << endl; 
    //save stuff... 
} 

void StringFieldOrm::migrate() 
{ 
    cout << "Migrate value " << m_value << endl; 
    //migrate stuff... 
} 

class IntFieldOrm : public IFieldOrm 
{ 
public: 
    IntFieldOrm(int& value) : m_value(value) {} 

    virtual void save(); 
    virtual void migrate(); 

private: 
    int m_value; 
}; 

void IntFieldOrm::save() 
{ 
    cout << "Save value " << m_value << endl; 
    //save stuff... 
} 

void IntFieldOrm::migrate() 
{ 
    cout << "Migrate value " << m_value << endl; 
    //migrate stuff 
} 



//and finally implement your final class 
//note that this object can be "dynamically extended" by inserting new fields, 
//you may want to prevent that and I can think of a solution if you want to 
class UserProfile: public BaseOrm 
{ 
public: 
    UserProfile(const string& username, const string& email, int age); 
}; 

UserProfile::UserProfile(const string& username, const string& email, int age) 
{ 
    m_fields["username"] = new StringFieldOrm(username); 
    m_fields["email"] = new StringFieldOrm(email); 
    m_fields["age"] = new IntFieldOrm(age); 
} 



int main(int argc, char* argv[]) 
{ 
    UserProfile user = UserProfile("Batman", "[email protected]", 30); 

    user.save(); 

    return 0; 
} 
-3

eine Variable Userprofile erstellen und auf sie zugreifen:

userProfile user; 
int main(){ 
std::cout << user.username; 
std::cout << user.email ; 
} 

das ist, wie man auf sie zugreifen würde, es sei denn aus verschiedenen Gründen, nicht um sie auf den Bildschirm zu drucken.

+0

userProfile wie ich schon erwähnte war nur ein Beispiel. Die Struktur einer Klasse kann variieren und ich brauche eine generische Methode, um Migrationen vorzunehmen und sie in der Basis-ORM-Klasse zu speichern :) –

+0

Das war nicht in Frage. –

+0

@xandercage Vielleicht sollten Sie einen anderen Titel verwenden? – juanchopanza

1

das Hinzufügen von Reflektion zu C++ ist nicht wahnsinnig schwierig, aber es erfordert eine einigermaßen gute Kenntnis des Vorlagentyps und einiger sorgfältiger Planung.

In diesem Arbeitsbeispiel habe ich einen Start für Sie gemacht. Dieses Framework unterstützt das Schreiben der Mitglieder in eine "Statement" -Klasse (Modellierung einer vorbereiteten Datenbankanweisung).

Ähnliche Techniken können verwendet werden, um die SQL-Generation für CRUD aufzubauen.

Zweifellos gibt es bereits Bibliotheken, die dies für Sie tun ...

#include <iostream> 
#include <iomanip> 
#include <string> 
#include <tuple> 
#include <utility> 

using namespace std; 

struct statement 
{ 
    void setString(int index, const std::string& value) 
    { 
     std::cout << "setting index " << index << " to value " << std::quoted(value) << std::endl; 
    } 
}; 


struct BaseOrm 
{ 
    virtual void serialise(statement& stmt) const = 0; 
}; 

template<class Class> 
struct class_tag { 
    using type = Class; 
}; 

template<const char* Name> 
struct name_tag { 
    static constexpr const char* name() { return Name; } 
}; 

namespace detail { 

    struct reflection_item_concept 
    { 
     virtual const std::string& name() const = 0; 
     virtual std::string to_archive_string(const void* object) const = 0; 
     virtual void from_archive_string(void* object, const std::string& as) const = 0; 
    }; 

    template<class T> 
    std::string to_archive_string_impl(const T& val) { 
     return std::to_string(val); 
    } 

    const std::string& to_archive_string_impl(const std::string& s) { 
     return s; 
    } 

    template<class NameTag, class Class, class Type> 
    struct reflection_item : reflection_item_concept 
    { 
     reflection_item(Type Class::* mfp) : mfp(mfp) {} 

     static const class_tag<Class> class_info() { return {}; }; 
     static const char* raw_name() { return NameTag::name(); }; 

     // concept implementation 
     const std::string& name() const override { 
      static const std::string s = raw_name(); 
      return s; 
     } 

     std::string to_archive_string(const void* object) const override 
     { 
      auto& val = (*reinterpret_cast<const Class*>(object)).*mfp; 
      return to_archive_string_impl(val); 
     } 

     void from_archive_string(void* item, const std::string& as) const override 
     { 
      // similar mechanism here 
     } 

     Type Class::* mfp; 
    }; 
} 

template<class NameTag, class Class, class Type> 
constexpr auto reflection_item(NameTag, Type Class::* mp) 
{ 
    return detail::reflection_item<NameTag, Class, Type> { mp }; 
} 

struct class_reflection_concept 
{ 
    virtual void serialise(const void* object, statement& stmt) const = 0; 
}; 

namespace detail { 

    template<class ClassTag, class...ReflectionItems> 
    struct reflection_impl : class_reflection_concept 
    { 
     reflection_impl(ReflectionItems...refs) 
     : _reflectors(std::make_tuple(refs...)) 
     {} 

     template<std::size_t...Is> 
     void serialise_impl(std::index_sequence<Is...>, const void* object, 
          statement& stmt) const 
     { 
      using expand = int[]; 
      void(expand{ 
       0, 
       (stmt.setString(Is + 1, std::get<Is>(_reflectors).to_archive_string(object)),0)... 
      }); 
     } 

     void serialise(const void* object, statement& stmt) const override 
     { 
      serialise_impl(std::make_index_sequence<sizeof...(ReflectionItems)>(), 
          object, stmt); 
     } 

     std::tuple<ReflectionItems...> _reflectors; 
    }; 

} 

template<class ClassTag, class...ReflectionItems> 
auto& make_reflection(ClassTag tag, ReflectionItems...items) 
{ 

    static const detail::reflection_impl<ClassTag, ReflectionItems...> _ { items... }; 
    return _; 
} 



const char txt_username[] = "username"; 
const char txt_email[] = "email"; 
const char txt_x[] = "x"; 

class userProfile: public BaseOrm 
{ 
public: 
    string username = "test username"; 
    string email = "[email protected]"; 
    int x = 10; 

    // implement serialisation 
    void serialise(statement& stmt) const override 
    { 
     reflection.serialise(this, stmt); 
    } 


    static const class_reflection_concept& reflection; 
}; 

const class_reflection_concept& userProfile::reflection = 
make_reflection(class_tag<userProfile>(), 
       reflection_item(name_tag<txt_username>(), &userProfile::username), 
       reflection_item(name_tag<txt_email>(), &userProfile::email), 
       reflection_item(name_tag<txt_x>(), &userProfile::x)); 

int main() 
{ 
    userProfile x; 
    statement stmt; 
    x.serialise(stmt); 

} 

erwarteten Ergebnisse:

setting index 1 to value "test username" 
setting index 2 to value "[email protected]" 
setting index 3 to value "10"