Commit 2c4c98e3 authored by Kenton Varda's avatar Kenton Varda

Support nested types in C++ generator.

parent b4a4ce90
......@@ -283,6 +283,20 @@ TEST(Encoding, UnionDefault) {
}
}
TEST(Encoding, NestedTypes) {
// This is more of a test of the generated code than the encoding.
MallocMessageBuilder builder;
TestNestedTypes::Reader reader = builder.getRoot<TestNestedTypes>().asReader();
EXPECT_EQ(TestNestedTypes::NestedEnum::BAR, reader.getOuterNestedEnum());
EXPECT_EQ(TestNestedTypes::NestedStruct::NestedEnum::QUUX, reader.getInnerNestedEnum());
TestNestedTypes::NestedStruct::Reader nested = reader.getNestedStruct();
EXPECT_EQ(TestNestedTypes::NestedEnum::BAR, nested.getOuterNestedEnum());
EXPECT_EQ(TestNestedTypes::NestedStruct::NestedEnum::QUUX, nested.getInnerNestedEnum());
}
} // namespace
} // namespace internal
} // namespace capnproto
......@@ -233,3 +233,26 @@ struct TestUnionDefaults {
s0sps1s32Set @1 :TestUnion =
(u0f1s0 = void, u1f0sp = "foo", u2f0s1 = true, u3f0s32 = 12345678);
}
struct TestNestedTypes {
enum NestedEnum {
foo @0;
bar @1;
}
struct NestedStruct {
enum NestedEnum {
baz @0;
qux @1;
quux @2;
}
outerNestedEnum @0 :TestNestedTypes.NestedEnum = bar;
innerNestedEnum @1 :NestedEnum = quux;
}
nestedStruct @0 :NestedStruct;
outerNestedEnum @1 :NestedEnum = bar;
innerNestedEnum @2 :NestedStruct.NestedEnum = quux;
}
......@@ -29,6 +29,8 @@ import qualified Data.ByteString.UTF8 as ByteStringUTF8
import Data.FileEmbed(embedFile)
import Data.Word(Word8)
import qualified Data.Digest.MD5 as MD5
import qualified Data.Map as Map
import Data.Maybe(catMaybes)
import Data.Binary.IEEE754(floatToWord, doubleToWord)
import Text.Printf(printf)
import Text.Hastache
......@@ -44,6 +46,28 @@ import WireFormat
-- instead.
muNull = MuBool False;
-- There is no way to make a MuType from a singular MuContext, i.e. for optional sub-contexts.
-- Using a single-element list has the same effect, though.
muJust c = MuList [c]
fullName desc = scopePrefix (descParent desc) ++ descName desc
scopePrefix (DescFile _) = ""
scopePrefix desc = fullName desc ++ "::"
globalName (DescFile _) = " " -- TODO: namespaces
globalName desc = globalName (descParent desc) ++ "::" ++ descName desc
-- Flatten the descriptor tree in pre-order, returning struct and interface descriptors only. We
-- skip enums because they are always declared directly in their parent scope.
flattenTypes :: [Desc] -> [Desc]
flattenTypes [] = []
flattenTypes (d@(DescStruct s):rest) = d:(flattenTypes children ++ flattenTypes rest) where
children = catMaybes $ Map.elems $ structMemberMap s
flattenTypes (d@(DescInterface i):rest) = d:(flattenTypes children ++ flattenTypes rest) where
children = catMaybes $ Map.elems $ interfaceMemberMap i
flattenTypes (_:rest) = flattenTypes rest
hashString :: String -> String
hashString str =
concatMap (printf "%02x" . fromEnum) $
......@@ -93,9 +117,9 @@ cxxTypeString (BuiltinType BuiltinFloat32) = "float"
cxxTypeString (BuiltinType BuiltinFloat64) = "double"
cxxTypeString (BuiltinType BuiltinText) = " ::capnproto::Text"
cxxTypeString (BuiltinType BuiltinData) = " ::capnproto::Data"
cxxTypeString (EnumType desc) = enumName desc -- TODO: full name
cxxTypeString (StructType desc) = structName desc -- TODO: full name
cxxTypeString (InterfaceType desc) = interfaceName desc -- TODO: full name
cxxTypeString (EnumType desc) = globalName $ DescEnum desc
cxxTypeString (StructType desc) = globalName $ DescStruct desc
cxxTypeString (InterfaceType desc) = globalName $ DescInterface desc
cxxTypeString (ListType t) = concat [" ::capnproto::List<", cxxTypeString t, ">"]
cxxFieldSizeString Size0 = "VOID";
......@@ -192,7 +216,7 @@ fieldContext parent desc = mkStrContext context where
context "fieldIsStructList" = MuBool $ isStructList $ fieldType desc
context "fieldDefaultBytes" =
case fieldDefaultValue desc >>= defaultValueBytes (fieldType desc) of
Just v -> MuList [defaultBytesContext context (fieldType desc) v]
Just v -> muJust $ defaultBytesContext context (fieldType desc) v
Nothing -> muNull
context "fieldType" = MuVariable $ cxxTypeString $ fieldType desc
context "fieldBlobType" = MuVariable $ blobTypeString $ fieldType desc
......@@ -205,7 +229,7 @@ fieldContext parent desc = mkStrContext context where
context "fieldElementType" =
MuVariable $ cxxTypeString $ elementType $ fieldType desc
context "fieldUnion" = case fieldUnion desc of
Just (u, _) -> MuList [unionContext context u]
Just (u, _) -> muJust $ unionContext context u
Nothing -> muNull
context "fieldUnionDiscriminant" = case fieldUnion desc of
Just (_, n) -> MuVariable n
......@@ -221,23 +245,44 @@ unionContext parent desc = mkStrContext context where
context "unionHasRetro" = MuBool $ unionHasRetro desc
context s = parent s
childContext parent name = mkStrContext context where
context "nestedName" = MuVariable name
context s = parent s
structContext parent desc = mkStrContext context where
context "structName" = MuVariable $ structName desc
context "structFullName" = MuVariable $ fullName (DescStruct desc)
context "structFields" = MuList $ map (fieldContext context) $ structFields desc
context "structUnions" = MuList $ map (unionContext context) $ structUnions desc
context "structDataSize" = MuVariable $ packingDataSize $ structPacking desc
context "structReferenceCount" = MuVariable $ packingReferenceCount $ structPacking desc
context "structChildren" = MuList [] -- TODO
context "structNestedEnums" =
MuList $ map (enumContext context) $ structNestedEnums desc
context "structNestedStructs" =
MuList $ map (childContext context . structName) $ structNestedStructs desc
context "structNestedInterfaces" =
MuList $ map (childContext context . interfaceName) $ structNestedInterfaces desc
context s = parent s
typeContext parent desc = mkStrContext context where
context "typeStruct" = case desc of
DescStruct d -> muJust $ structContext context d
_ -> muNull
context "typeEnum" = case desc of
DescEnum d -> muJust $ enumContext context d
_ -> muNull
context s = parent s
fileContext desc = mkStrContext context where
flattenedMembers = flattenTypes $ catMaybes $ Map.elems $ fileMemberMap desc
context "fileName" = MuVariable $ fileName desc
context "fileBasename" = MuVariable $ takeBaseName $ fileName desc
context "fileIncludeGuard" = MuVariable $
"CAPNPROTO_INCLUDED_" ++ hashString (fileName desc)
context "fileNamespaces" = MuList [] -- TODO
context "fileEnums" = MuList $ map (enumContext context) $ fileEnums desc
context "fileStructs" = MuList $ map (structContext context) $ fileStructs desc
context "fileTypes" = MuList $ map (typeContext context) flattenedMembers
context s = error ("Template variable not defined: " ++ s)
headerTemplate :: String
......
......@@ -33,14 +33,26 @@ Template for generated C++ header files.
namespace {{namespaceName}} {
{{/fileNamespaces}}
{{! =========================================================================================== }}
{{#fileStructs}}
{{#fileTypes}}
{{#typeStruct}}
struct {{structName}} {
struct {{structFullName}} {
class Reader;
class Builder;
{{#structChildren}}
struct {{structChildName}};
{{/structChildren}}
{{#structNestedStructs}}
struct {{nestedName}};
{{/structNestedStructs}}
{{#structNestedInterfaces}}
struct {{nestedName}};
{{/structNestedInterfaces}}
{{#structNestedEnums}}
enum class {{enumName}}: uint16_t {
{{#enumValues}}
{{enumValueName}} = {{enumValueNumber}},
{{/enumValues}}
};
{{/structNestedEnums}}
static constexpr ::capnproto::internal::StructSize STRUCT_SIZE =
::capnproto::internal::StructSize({{structDataSize}} * ::capnproto::WORDS,
......@@ -60,7 +72,8 @@ struct {{structName}} {
};
{{/structUnions}}
};
{{/fileStructs}}
{{/typeStruct}}
{{/fileTypes}}
{{! ------------------------------------------------------------------------------------------- }}
{{#fileEnums}}
......@@ -71,9 +84,10 @@ enum class {{enumName}}: uint16_t {
};
{{/fileEnums}}
{{! =========================================================================================== }}
{{#fileStructs}}
{{#fileTypes}}
{{#typeStruct}}
class {{structName}}::Reader {
class {{structFullName}}::Reader {
public:
Reader() = default;
inline explicit Reader(::capnproto::internal::StructReader base): _reader(base) {}
......@@ -101,11 +115,13 @@ public:
private:
::capnproto::internal::StructReader _reader;
};
{{/fileStructs}}
{{/typeStruct}}
{{/fileTypes}}
{{! ------------------------------------------------------------------------------------------- }}
{{#fileStructs}}
{{#fileTypes}}
{{#typeStruct}}
class {{structName}}::Builder {
class {{structFullName}}::Builder {
public:
Builder() = default;
inline explicit Builder(::capnproto::internal::StructBuilder base): _builder(base) {}
......@@ -152,21 +168,23 @@ public:
private:
::capnproto::internal::StructBuilder _builder;
};
{{/fileStructs}}
{{/typeStruct}}
{{/fileTypes}}
{{! =========================================================================================== }}
{{#fileStructs}}
{{#fileTypes}}
{{#typeStruct}}
{{#structUnions}}
// {{structName}}.{{unionDecl}}
inline {{structName}}::{{unionTitleCase}} {{structName}}::Reader::which{{unionTitleCase}}() {
// {{structFullName}}.{{unionDecl}}
inline {{structFullName}}::{{unionTitleCase}} {{structFullName}}::Reader::which{{unionTitleCase}}() {
return _reader.getDataField<{{unionTitleCase}}>({{unionTagOffset}} * ::capnproto::ELEMENTS);
}
{{/structUnions}}
{{#structFields}}
// {{structName}}.{{fieldDecl}}
// {{structFullName}}.{{fieldDecl}}
{{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
inline {{fieldType}} {{structFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -176,7 +194,7 @@ inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
}
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
inline {{fieldType}}::Reader {{structFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -190,7 +208,7 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
}
{{/fieldIsBlob}}
{{#fieldIsStruct}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
inline {{fieldType}}::Reader {{structFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -202,7 +220,7 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
}
{{/fieldIsStruct}}
{{#fieldIsList}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
inline {{fieldType}}::Reader {{structFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -215,21 +233,23 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
}
{{/fieldIsList}}
{{/structFields}}
{{/fileStructs}}
{{/typeStruct}}
{{/fileTypes}}
{{! =========================================================================================== }}
{{#fileStructs}}
{{#fileTypes}}
{{#typeStruct}}
{{#structUnions}}
// {{structName}}.{{unionDecl}}
inline {{structName}}::{{unionTitleCase}} {{structName}}::Builder::which{{unionTitleCase}}() {
// {{structFullName}}.{{unionDecl}}
inline {{structFullName}}::{{unionTitleCase}} {{structFullName}}::Builder::which{{unionTitleCase}}() {
return _builder.getDataField<{{unionTitleCase}}>({{unionTagOffset}} * ::capnproto::ELEMENTS);
}
{{/structUnions}}
{{#structFields}}
// {{structName}}.{{fieldDecl}}
// {{structFullName}}.{{fieldDecl}}
{{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Builder::get{{fieldTitleCase}}() {
inline {{fieldType}} {{structFullName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -237,7 +257,7 @@ inline {{fieldType}} {{structName}}::Builder::get{{fieldTitleCase}}() {
return _builder.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
}
inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value) {
inline void {{structFullName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
......@@ -247,7 +267,7 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value)
}
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
inline {{fieldType}}::Builder {{structFullName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -258,14 +278,14 @@ inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}nullptr, 0 * ::capnproto::BYTES{{/fieldDefaultBytes}});
}
inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}}::Reader value) {
inline void {{structFullName}}::Builder::set{{fieldTitleCase}}({{fieldType}}::Reader value) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
{{/fieldUnion}}
_builder.set{{fieldBlobType}}Field({{fieldOffset}} * ::capnproto::REFERENCES, value);
}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
inline {{fieldType}}::Builder {{structFullName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
......@@ -275,7 +295,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
}
{{/fieldIsBlob}}
{{#fieldIsStruct}}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}() {
inline {{fieldType}}::Builder {{structFullName}}::Builder::init{{fieldTitleCase}}() {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
......@@ -283,7 +303,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}()
return {{fieldType}}::Builder(_builder.initStructField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::STRUCT_SIZE));
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
inline {{fieldType}}::Builder {{structFullName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -295,7 +315,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
}
{{/fieldIsStruct}}
{{#fieldIsNonStructList}}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
inline {{fieldType}}::Builder {{structFullName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
......@@ -305,7 +325,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
::capnproto::internal::FieldSize::{{fieldElementSize}},
size * ::capnproto::ELEMENTS));
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
inline {{fieldType}}::Builder {{structFullName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -316,7 +336,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{^fieldDefaultBytes}}nullptr{{/fieldDefaultBytes}}));
}
template <typename _t>
inline void {{structName}}::Builder::set{{fieldTitleCase}}(const _t& other) {
inline void {{structFullName}}::Builder::set{{fieldTitleCase}}(const _t& other) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
......@@ -324,7 +344,7 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}(const _t& other) {
init{{fieldTitleCase}}(other.size()).copyFrom(other);
}
{{#fieldIsPrimitiveList}}
inline void {{structName}}::Builder::set{{fieldTitleCase}}(
inline void {{structFullName}}::Builder::set{{fieldTitleCase}}(
std::initializer_list<{{fieldElementType}}> other) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
......@@ -334,7 +354,7 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}(
}
{{/fieldIsPrimitiveList}}
{{^fieldIsPrimitiveList}}
inline void {{structName}}::Builder::set{{fieldTitleCase}}(
inline void {{structFullName}}::Builder::set{{fieldTitleCase}}(
std::initializer_list<{{fieldElementType}}::Reader> other) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
......@@ -345,7 +365,7 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}(
{{/fieldIsPrimitiveList}}
{{/fieldIsNonStructList}}
{{#fieldIsStructList}}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
inline {{fieldType}}::Builder {{structFullName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
......@@ -354,7 +374,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
{{fieldOffset}} * ::capnproto::REFERENCES, size * ::capnproto::ELEMENTS,
{{fieldElementType}}::STRUCT_SIZE));
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
inline {{fieldType}}::Builder {{structFullName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
......@@ -366,7 +386,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
}
{{/fieldIsStructList}}
{{/structFields}}
{{/fileStructs}}
{{/typeStruct}}
{{/fileTypes}}
{{! =========================================================================================== }}
{{#fileNamespaces}}
......
......@@ -31,18 +31,20 @@ Template for generated C++ source files.
namespace {{namespaceName}} {
{{/fileNamespaces}}
{{#fileStructs}}
constexpr ::capnproto::internal::StructSize {{structName}}::STRUCT_SIZE;
{{#fileTypes}}
{{#typeStruct}}
constexpr ::capnproto::internal::StructSize {{structFullName}}::STRUCT_SIZE;
{{#structFields}}
{{#fieldDefaultBytes}}
const ::capnproto::internal::AlignedData<{{defaultWordCount}}>
{{structName}}::DEFAULT_{{fieldUpperCase}} = {
{{structFullName}}::DEFAULT_{{fieldUpperCase}} = {
{ {{defaultByteList}} }
};
{{/fieldDefaultBytes}}
{{/structFields}}
{{/fileStructs}}
{{/typeStruct}}
{{/fileTypes}}
{{#fileNamespaces}}
} // namespace
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment