Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum support in C/C++ #41

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 73 additions & 27 deletions internal/generator/c/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ import (
"github.com/objectbox/objectbox-generator/internal/generator/model"
)

type fbsModel struct {
Enums []*fbsEnum
}

func (mm *fbsModel) Merge(model *model.ModelInfo) model.ModelMeta {
return mm
}

type fbsObject struct {
*binding.Object
fbsObject *reflection.Object
Expand Down Expand Up @@ -67,33 +75,6 @@ func (mo *fbsObject) CppNamespacePrefix() string {
return cppNamespacePrefix(mo.Namespace)
}

// CppNamespaceStart returns c++ namespace opening declaration
func (mo *fbsObject) CppNamespaceStart() string {
if len(mo.Namespace) == 0 {
return ""
}

var nss = strings.Split(mo.Namespace, ".")
for i, ns := range nss {
nss[i] = "namespace " + ns + " {"
}
return strings.Join(nss, "\n")
}

// CppNamespaceEnd returns c++ namespace closing declaration
func (mo *fbsObject) CppNamespaceEnd() string {
if len(mo.Namespace) == 0 {
return ""
}
var result = ""
var nss = strings.Split(mo.Namespace, ".")
for _, ns := range nss {
// print in reversed order
result = "} // namespace " + ns + "\n" + result
}
return result
}

// PreDeclareCppRelTargets returns C++ struct pre-declarations for related entities.
func (mo *fbsObject) PreDeclareCppRelTargets() (string, error) {
// first create a map `(ns.entity) => bool`, then sort it to keep the code from changing, and generate the C++ decl.
Expand Down Expand Up @@ -140,6 +121,7 @@ func (mo *fbsObject) PreDeclareCppRelTargets() (string, error) {

type fbsField struct {
*binding.Field
fbsModel *fbsModel
fbsField *reflection.Field
}

Expand All @@ -159,6 +141,10 @@ func (mp *fbsField) CppNameRelationTarget() string {
return cppNamespacePrefix(mp.relTargetNamespace()) + cppName(mp.ModelProperty.RelationTarget)
}

func isEnumSupportedType(t reflection.BaseType) bool {
return t >= reflection.BaseTypeByte && t <= reflection.BaseTypeULong
}

// CppType returns C++ type name
func (mp *fbsField) CppType() string {
var fbsType = mp.fbsField.Type(nil)
Expand All @@ -168,10 +154,21 @@ func (mp *fbsField) CppType() string {
cppType = cppType + "<" + fbsTypeToCppType[fbsType.Element()] + ">"
} else if (mp.ModelProperty.IsIdProperty() || mp.ModelProperty.Type == model.PropertyTypeRelation) && cppType == "uint64_t" {
cppType = "obx_id" // defined in objectbox.h
} else if enumIndex := mp.enumIndex(); enumIndex >= 0 {
cppType = strings.ReplaceAll(string(mp.fbsModel.Enums[enumIndex].enum.Name()), ".", "::")
}
return cppType
}

func (mp *fbsField) enumIndex() int {
var fbsType = mp.fbsField.Type(nil)
var baseType = fbsType.BaseType()
if isEnumSupportedType(baseType) {
return int(fbsType.Index())
}
return -1
}

// CppFbType returns C++ type name used in flatbuffers templated functions
func (mp *fbsField) CppFbType() string {
var cppType = mp.CppType()
Expand Down Expand Up @@ -273,6 +270,9 @@ func (mp *fbsField) FbDefaultValue() string {
case model.PropertyTypeDouble:
return "0.0"
}
if enumIndex := mp.enumIndex(); enumIndex >= 0 {
return fmt.Sprintf("static_cast<%s>(0)", mp.CppFbType())
}
return "0"
}

Expand Down Expand Up @@ -301,3 +301,49 @@ func (mr *standaloneRel) Merge(rel *model.StandaloneRelation) model.StandaloneRe
func (mr *standaloneRel) CppName() string {
return cppName(mr.ModelRelation.Name)
}

type fbsEnum struct {
enum *reflection.Enum
Values []*reflection.EnumVal
}

func (me *fbsEnum) nameParts() (ns string, name string) {
var components = strings.Split(string(me.enum.Name()), ".")
ns = strings.Join(components[0:len(components)-1], ".")
name = components[len(components)-1]
return
}

func (me *fbsEnum) Namespace() string {
ns, _ := me.nameParts()
return ns
}

func (me *fbsEnum) Name() string {
_, name := me.nameParts()
return name
}

func (me *fbsEnum) UnderlyingCppType() string {
var baseType = me.enum.UnderlyingType(nil).BaseType()
return fbsTypeToCppType[baseType]
}

func (me *fbsEnum) Comments() []string {
var comments []string
for i := 0; i < me.enum.DocumentationLength(); i++ {
comments = append(comments, strings.TrimSpace(string(me.enum.Documentation(i))))
}
return comments
}

func (me *fbsEnum) HasAttribute(name string) bool {
var kv = reflection.KeyValue{}
var i = 0
for me.enum.Attributes(&kv, i) {
if string(kv.Key()) == name {
return true
}
}
return false
}
17 changes: 16 additions & 1 deletion internal/generator/c/schema-reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ type fbSchemaReader struct {
// const annotationPrefix = "objectbox:"

func (r *fbSchemaReader) read(schema *reflection.Schema) error {
var fbsmodel = &fbsModel{Enums: make([]*fbsEnum, schema.EnumsLength())}
r.model.Meta = fbsmodel
for i := range fbsmodel.Enums {
fbsmodel.Enums[i] = &fbsEnum{&reflection.Enum{}, []*reflection.EnumVal{}}
if !schema.Enums(fbsmodel.Enums[i].enum, i) {
return fmt.Errorf("can't access enum %d", i)
}
for j := 0; j < fbsmodel.Enums[i].enum.ValuesLength(); j++ {
fbsmodel.Enums[i].Values = append(fbsmodel.Enums[i].Values, &reflection.EnumVal{})
if !fbsmodel.Enums[i].enum.Values(fbsmodel.Enums[i].Values[j], j) {
return fmt.Errorf("can't access enum %s value %d", string(fbsmodel.Enums[i].enum.Name()), j)
}
}
}

for i := 0; i < schema.ObjectsLength(); i++ {
var object reflection.Object
if !schema.Objects(&object, i) {
Expand Down Expand Up @@ -132,7 +147,7 @@ func (r *fbSchemaReader) readObject(object *reflection.Object) error {

func (r *fbSchemaReader) readObjectField(entity *model.Entity, field *reflection.Field) error {
var property = model.CreateProperty(entity, 0, 0)
var metaProperty = &fbsField{binding.CreateField(property), field}
var metaProperty = &fbsField{binding.CreateField(property), r.model.Meta.(*fbsModel), field}
property.Meta = metaProperty
metaProperty.SetName(string(field.Name()))

Expand Down
10 changes: 8 additions & 2 deletions internal/generator/c/templates/binding-c.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,20 @@ static void* {{.FileIdentifier}}_get_object(OBX_box* box, obx_id id, void* (*fro

/// Internal function used in other generated functions to get a vTable offset for a given field.
static flatbuffers_voffset_t {{.FileIdentifier}}_fb_field_offset(flatbuffers_voffset_t vs, const flatbuffers_voffset_t* vt, size_t field);

{{range $enum := .Model.Meta.Enums}}
typedef enum {
{{- range $value := $enum.Values}}
{{with CNamespace $enum.Namespace}}{{.}}_{{end}}{{$enum.Name}}_{{String $value.Name}} = {{$value.Value}},
{{- end}}
} {{with CNamespace $enum.Namespace}}{{.}}_{{end}}{{$enum.Name}};
{{end}}
{{range $entity := .Model.EntitiesWithMeta}}
{{PrintComments 0 $entity.Comments}}typedef struct {{$entity.Meta.CName}} {
{{range $property := $entity.Properties}}{{$propType := PropTypeName $property.Type -}}
{{PrintComments 1 $property.Comments}}{{if $property.Meta.FbIsVector}}{{$property.Meta.CElementType}}* {{$property.Meta.CppName}};
{{- if or (eq $propType "StringVector") (eq $propType "ByteVector")}}
size_t {{$property.Meta.CppName}}_len;{{end}}
{{else}}{{$property.Meta.CppType}}{{if $property.Meta.Optional}}*{{end}} {{$property.Meta.CppName}};
{{else}}{{CNamespace $property.Meta.CppType}}{{if $property.Meta.Optional}}*{{end}} {{$property.Meta.CppName}};
{{end}}{{end}}
} {{$entity.Meta.CName}};

Expand Down
17 changes: 15 additions & 2 deletions internal/generator/c/templates/binding-hpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,22 @@ var CppBindingTemplateHeader = template.Must(template.New("binding-hpp").Funcs(f
#include "flatbuffers/flatbuffers.h"
#include "objectbox.h"
#include "objectbox.hpp"
{{- range $enum := .Model.Meta.Enums}}
{{with CppNamespaceStart $enum.Namespace}}
{{.}}{{end}}
enum class {{$enum.Name}} : {{$enum.UnderlyingCppType}} {
{{- range $value := $enum.Values}}
{{String $value.Name}} = {{$value.Value}},
{{- end}}
};
{{- if $enum.HasAttribute "bit_flags"}}
FLATBUFFERS_DEFINE_BITMASK_OPERATORS({{$enum.Name}}, {{$enum.UnderlyingCppType}})
{{- end}}
{{with CppNamespaceEnd $enum.Namespace}}{{.}}{{end -}}
{{end}}
{{range $entity := .Model.EntitiesWithMeta}}
{{$entity.Meta.PreDeclareCppRelTargets -}}
{{with $entity.Meta.CppNamespaceStart}}
{{with CppNamespaceStart $entity.Meta.Namespace}}
{{.}}{{end}}
struct {{$entity.Meta.CppName}}_;

Expand Down Expand Up @@ -81,6 +94,6 @@ struct {{$entity.Meta.CppName}}_ {
static const obx::RelationStandalone<{{$entity.Meta.CppName}}, {{$relation.Target.Meta.CppName}}> {{$relation.Meta.CppName}};
{{- end}}
};
{{with $entity.Meta.CppNamespaceEnd}}{{.}}{{end -}}
{{with CppNamespaceEnd $entity.Meta.Namespace}}{{.}}{{end -}}
{{end}}
`))
29 changes: 29 additions & 0 deletions internal/generator/c/templates/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,33 @@ var funcMap = template.FuncMap{
"IsOptionalPtr": func(optional string) bool {
return optional == "std::unique_ptr" || optional == "std::shared_ptr"
},
"CNamespace": func (ns string) string {
return strings.ReplaceAll(strings.ReplaceAll(ns, ".", "_"), "::", "_")
},
"CppNamespaceStart": func(ns string) string {
if len(ns) == 0 {
return ""
}

var nss = strings.Split(ns, ".")
for i, ns := range nss {
nss[i] = "namespace " + ns + " {"
}
return strings.Join(nss, "\n")
},
"CppNamespaceEnd": func(ns string) string {
if len(ns) == 0 {
return ""
}
var result = ""
var nss = strings.Split(ns, ".")
for _, ns := range nss {
// print in reversed order
result = "} // namespace " + ns + "\n" + result
}
return result
},
"String": func(val []byte) string {
return string(val)
},
}
6 changes: 6 additions & 0 deletions internal/generator/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ func mergeBindingWithModelInfo(currentModel *model.ModelInfo, storedModel *model
currentModel.LastIndexId = storedModel.LastIndexId
currentModel.LastRelationId = storedModel.LastRelationId

if currentModel.Meta != nil {
storedModel.Meta = currentModel.Meta.Merge(storedModel)
} else {
storedModel.Meta = nil
}

return nil
}

Expand Down
6 changes: 6 additions & 0 deletions internal/generator/model/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

package model

// ModelMeta provides a way for bindings to provide additional information to other users of ModelInfo
type ModelMeta interface {
// Merge produces new ModelMeta based on its internal state and given model
Merge(model *ModelInfo) ModelMeta
}

// EntityMeta provides a way for bindings to provide additional information to other users of Entity
type EntityMeta interface {
// Merge produces new EntityMeta based on its internal state and given entity
Expand Down
1 change: 1 addition & 0 deletions internal/generator/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type ModelInfo struct {

file *os.File // file handle, locked while the model is open
Rand *rand.Rand `json:"-"` // seeded random number generator
Meta ModelMeta `json:"-"`
}

var defaultModel = ModelInfo{
Expand Down
Loading