您好我正在尝试在c ++中为项目创建一个简单的ORM。对于此示例,假设一个简单的类为
class userProfile: public BaseOrm
{
public:
string username;
string email;
};
现在base orm有一个方法save()和migrate()。我想要的是当一个人调用migrate()所有模式时,在这种情况下,用户名和电子邮件被填充为db表,并且在保存时它们会持久保存在数据库中。
我遇到的问题是如何获得类中定义的所有字段,例如本例username
和email
以及此类型中的字符串类型。任何帮助将不胜感激。
我知道c ++中没有反映,所以我实际上并不关心变量名称,而是更多关于变量的数量以及用DB来映射它们的类型。
答案 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 ;
}
这是您访问它们的方式,除了不同的原因,而不是将它们打印到屏幕上。