2016-07-06 60 views
2

嗨,我想在一个项目的C++中创建一个简单的ORM。对于这个例子假设一个简单的类在C++中访问类中的字段和类型列表

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

现在基地orm有一个方法save()和migrate()。我想要的是当一个人调用migrate()所有的模式,在这种情况下,用户名和电子邮件被填充为数据库表,并保存它们保持在数据库上。

我遇到的问题是如何获得所有字段在类中定义的内容,例如usernameemail以及此处的字符串类型。任何帮助,将不胜感激。

我知道在C++中没有反射,所以我并不真正关心的变量名,但更多的变量有类型将它们与数据库映射的数量。

+0

您可以通过'userProfile'作为模板参数'BaseOrm'。 –

+2

您应该在自定义类中重载/覆盖'save'和'migrate'函数,并在合适的位置调用相应函数的基本版本。 – Arunmu

+0

@πάνταῥεῖ如果你不介意,请给我一个例子:) –

回答

0

我的理解是,你要为它具有类的一般行为变量集合的字段。

我建议你创建将被保存在你的基类的容器(例如地图[fieldName, fieldInterface])一个“场”的界面。你仍然必须为每个字段的类型实现一个行为,但是你可以创建任何从基类派生的具有动态字段集合的类。

下面是一个例子:

#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

创建USERPROFILE变量和访问它们:

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

这是你将如何访问它们,但出于不同的原因,不是他们打印到屏幕上。

+0

userProfile正如我所提到的只是一个例子。一个类的结构可能会有所不同,我需要一个通用的方法来进行迁移并将它们保存在基本的ORM类:) –

+0

这不是问题。 –

+0

@xandercage也许你应该使用不同的标题? – juanchopanza

1

增加反射C++并不出奇困难,但它确实需要的模板类型推演和一些精心策划的一个相当不错的知识。

在此工作示例我已经开始为您服务。该框架支持将成员写入“语句”类(对数据库准备语句进行建模)。

类似的技术可用于打造出来的SQL生成CRUD。

毫无疑问,已经为你做这个图书馆...

#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); 

} 

预计业绩:

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