使用c ++

时间:2016-07-06 06:24:57

标签: c++ orm

您好我正在尝试在c ++中为项目创建一个简单的ORM。对于此示例,假设一个简单的类为

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

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

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

我知道c ++中没有反映,所以我实际上并不关心变量名称,而是更多关于变量的数量以及用DB来映射它们的类型。

3 个答案:

答案 0 :(得分:1)

向c ++添加反射并不是非常困难,但它确实需要相当好的模板类型推导知识和一些仔细的规划。

在这个工作示例中,我为你做了一个开始。该框架支持将成员写入"语句" class(建模数据库预处理语句)。

可以使用类似的技术为CRUD构建SQL生成。

毫无疑问,已有图书馆为您做这件事......

#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 = "noone@nowhere.com";
    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 "noone@nowhere.com"
setting index 3 to value "10"

答案 1 :(得分:0)

我的理解是你想要一个具有一组可变字段的类的通用行为。

我建议你创建一个“field”接口,它将使用容器(例如[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", "bw@batmail.com", 30);

    user.save();

    return 0;
}

答案 2 :(得分:-3)

创建一个userProfile变量并访问它们:

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

这是您访问它们的方式,除了不同的原因,而不是将它们打印到屏幕上。