2016-10-12 18 views
0

Я работаю над диссертацией бакалавра. Цель состоит в том, чтобы написать базу данных объектов (похожую на CoreData) на C++. Одним из требований является поддержка рефлексивных отношений (1-1,1-M, M-M) и загрузка динамического объекта.База данных объектов на C++

Мой нынешний дизайн состоит из простого языка DDL с генерированием кода. Пользователь пишет свои классы, которые он хочет сохранить, а затем пишет отношения между этими классами. Что-то вроде этого:

Person { 
    string name; 
    int salary; 
} 
relation Person.boss(1) references Person inverse Person.subs(M); 

Из этого я создаю заголовок C++ с определением класса, источником C++ с определениями методов. Класс имеет все примитивные поля как общедоступные, отношения являются частными и доступны только с помощью методов get/set или get/add/remove/clear. В этих методах я сохраняю последовательность рефлексивных отношений. Например: в методе setBoss я хотел бы сделать следующее:

void setBoss(ptr<Person> val) { 
     if (boss) { 
      boss->subs.remove(this); 
     } 
     boss = val; 
     boss->subs.add(this); 
     boss.modified = true 
    } 

Этот код генерируется все, и работает. Но мой руководитель требует, чтобы я делал это без какой-либо генерации кода и старался следовать за CoreData как можно ближе. Я думаю, его идея состоит в том, чтобы имитировать динамический объект в C++, где каждый объект, который может быть сохранен в базе данных, содержит map<string,value> и поля, считанные с этой карты. Я думаю, что этот дизайн явно неверен для C++, так как для хранения всего, что требуется для этого, требуется, чтобы каждое поле было настраиваемым классом, который ссылается на владельца и динамический при каждом доступе к полю, и я даже не говорю о сохранении других классов, которые используют пользовательский общий указатель (это шаблон, поэтому динамическое приведение не будет работать).

Кроме того, существует проблема с метаданными, как определить схему в этой системе? Вероятно, я мог бы использовать посетителя, который посещает все поля, а использование какого-то макроопределения получает их имя и тип, но рефлексивные отношения? Я не знаю.

В моем подходе я могу как-то добавить дополнительные поля и создать для них код миграции. (сохранение версии схемы внутри постоянной базы данных, затем, когда я открываю эту базу данных, проверяю версию и запускает сгенерированный код миграции).

Что-то я здесь не хватает? Является ли мой подход совершенно неправильным?

ответ

0

[конечно, ниже мнение, но винить вопрос, а не я]

Есть ли что-то я здесь отсутствует? Является ли мой подход совершенно неправильным?

Совершенно неправильно - нет. Но не лишены рисков.

Один из недостатков вашего подхода (ORM-подобный): каждый раз, когда вы меняете структуры, «миграция хранилища данных» будет кошмаром. Или вам придется навязать пользователям (товарищам программистов) кошмар, связанный с различными версиями схемы.

Посмотрите на Google на предмет проблем с генерируемыми структурами, переживающими эволюцию, - они не имеют дело с хранением, просто с передачей данных. И все же вам нужно тщательно перемещаться (какие члены требуются/необязательны) и подчиняться строгим правилам (например, между версиями вашего протокола, просто добавьте, но никогда не удаляйте поля).

Кроме того, существует проблема с метаданными, как определить схему в этой системе?

Разве это не замечательная тема для диссертации? Конечно, это не будет тривиально, но почему это должно быть?

+0

Ну, с моим подходом создание схемы довольно выполнимо, так как я генерирую код, я могу сгенерировать метод, который принимает некоторый «контекстный» объект и вызывает методы «generateTable», 'addAttribute' в этом контексте. Возможны также миграции (добавьте добавленные поля в DDL, затем сгенерируйте с ними код миграции). С моим подходом руководителя, не так много. – semtexzv

+0

«Возможно также миграция (добавьте добавленные поля в DDL, затем сгенерируйте код миграции)» Я прошу не согласиться. Если ваши изменения являются только дополнениями, то * конечно * все будет хорошо. Однако попробуйте изменить 1-M на M-M и посмотреть, что это будет делать с кодом, вызывающим вашу более старую версию. «С моим подходом начальника, не так много». С вашим подходом руководителя нет необходимости. –

+0

Я не понимаю, всегда будет необходимость обновлять схему после миграции. – semtexzv

0

Без генерации кода вы должны выбрать между подходом с компилированным уровнем - с использованием DSL или ориентированного на выполнение времени подхода - с использованием некоторой комбинации виртуальных функций, динамического каста и typeid/typeindex.

Упражнение DSL - это то, что мы пережили в этом году, и, по иронии судьбы, оно очень близко относится к вашему вопросу: 1-1, 1-N, N-N коллекций. Конечно, это было бы излишним тестом для бакалавриата, но вот как это может выглядеть: https://github.com/C5T/Current/blob/1b9716438d323b8b47eb8d5bf5907e98ee3be1dc/Storage/test.cc#L92-L120

В вашем случае я хотел бы сначала спросить у супервизора, ищут ли они время компиляции или время выполнения. Время выполнения, вероятно, проще реализовать временным и LOC-мудрым, но оно будет предлагать значительно меньше гарантий, и результат будет менее практичным. Для строк < => сопоставлений строк только, хотя пара коллекций и виртуальные классы с политикой могут хорошо выполнять эту работу.

+0

Я пробовал DSL, но для создания рефлексивных отношений потребуется * LOT * макросов, вместо этого я решил использовать собственный язык с инструментом для генерации необходимого кода. И да, после последней встречи с ним у меня появилась идея, что он хочет, чтобы я сделал все динамическое, что-то вроде класса DbVal, из которого 'DbInt, DbFloat, DbString' наследует и хранит' map 'внутри объекта как хранение данных, но он также хочет, чтобы атрибуты могли быть восстановлены с помощью полей acces, это означает, что поля класса должны иметь ссылку на класс владельца, и чтение из этого поля будет считано с указанной карты. – semtexzv

+0

Это отличное упражнение. Я был бы благодарен за такого учителя. – Dima