Ejemplo n.º 1
0
    def generate_rule_of_five(self):

        raw_pointer = [v.name for v in self.fields if v.pointer and v.pointer == PointerType['RAW']]
        raw_pointer_warning = '#pragma message ( "WARNING: The following fields in {0} are raw pointers and copying or moving will copy the raw pointer address: {1}") \n'.format(
            self.name, ', '.join(raw_pointer)
        )

        rule_of_five = dedent(
            """\
            {warning}
            {name}(const {name}&) = default;
            {name}({name}&&) = default;
            ~{name}() = default;
            {name}& operator=(const {name}&) = default;
            {name}& operator=({name}&&) = default;"""
        )

        return (rule_of_five.format(name=self.name, warning=raw_pointer_warning if raw_pointer else ''), '')
Ejemplo n.º 2
0
            if function not in duplicates:
                duplicates.append(function)
                functions.append(function)

# Write our file
with open(base_file, 'w') as f:

    f.write(dedent("""\
        #include <pybind11/pybind11.h>
        #include <pybind11/complex.h>
        #include <pybind11/stl.h>
        #include <pybind11/eigen.h>

        // Declare our functions (that we know will be made later)
        {function_declarations}

        PYBIND11_PLUGIN(message) {{

            pybind11::module module("message", "NUClear message classes");

            // Initialise each of the modules
        {function_calls}

            return module.ptr();
        }}
        """).format(
            function_declarations='\n'.join('void init_message_{}(pybind11::module& module);'.format(f) for f in functions),
            function_calls=indent('\n'.join('init_message_{}(module);'.format(f) for f in functions)),
        ))

Ejemplo n.º 3
0
    def generate_cpp(self):

        # Make our value pairs
        values = indent('\n'.join(['{} = {}'.format(v[0], v[1]) for v in self.values]), 8)
        values = ',\n'.join([v for v in values.splitlines()])

        scope_name='_'.join(self.fqn.split('.'))

        # Make our switch statement pairs
        switches = indent('\n'.join(['case Value::{}: return "{}";'.format(v[0], v[0]) for v in self.values]), 8)

        # Make our if chain
        if_chain = indent('\nelse '.join(['if (str == "{}") value = Value::{};'.format(v[0], v[0]) for v in self.values]))

        # Get our default value
        default_value = dict([reversed(v) for v in self.values])[0]

        # Make our fancy enums
        header_template = dedent("""\
            struct {name} : public ::message::MessageBase<{name}> {{
                enum Value {{
            {values}
                }};
                Value value;

                // Constructors
                {name}();

                {name}(int const& v);

                {name}(Value const& value);

                {name}(std::string const& str);

                {name}({protobuf_name} const& p);

                // Operators
                bool operator <({name} const& other) const;

                bool operator >({name} const& other) const;

                bool operator <=({name} const& other) const;

                bool operator >=({name} const& other) const;

                bool operator ==({name} const& other) const;

                bool operator !=({name} const& other) const;

                bool operator <({name}::Value const& other) const;

                bool operator >({name}::Value const& other) const;

                bool operator <=({name}::Value const& other) const;

                bool operator >=({name}::Value const& other) const;

                bool operator ==({name}::Value const& other) const;

                bool operator !=({name}::Value const& other) const;

                // Conversions
                operator Value() const;

                operator int() const;

                operator std::string() const;

                operator {protobuf_name}() const;

                friend std::ostream& operator<< (std::ostream& out, const {name}& val);
            }};""")

        impl_template = dedent("""\
            typedef {fqn} T{scope_name};

            {fqn}::{name}() : value(Value::{default_value}) {{}}

            {fqn}::{name}(int const& v) : value(static_cast<Value>(v)) {{}}

            {fqn}::{name}(Value const& value) : value(value) {{}}

            {fqn}::{name}(std::string const& str) {{
            {if_chain}
                else throw std::runtime_error("String " + str + " did not match any enum for {name}");
            }}

            {fqn}::{name}({protobuf_name} const& p) {{
                value = static_cast<Value>(p);
            }}

            bool {fqn}::operator <({name} const& other) const {{
                return value < other.value;
            }}

            bool {fqn}::operator >({name} const& other) const {{
                return value > other.value;
            }}

            bool {fqn}::operator <=({name} const& other) const {{
                return value <= other.value;
            }}

            bool {fqn}::operator >=({name} const& other) const {{
                return value >= other.value;
            }}

            bool {fqn}::operator ==({name} const& other) const {{
                return value == other.value;
            }}

            bool {fqn}::operator !=({name} const& other) const {{
                return value != other.value;
            }}

            bool {fqn}::operator <({name}::Value const& other) const {{
                return value < other;
            }}

            bool {fqn}::operator >({name}::Value const& other) const {{
                return value > other;
            }}

            bool {fqn}::operator <=({name}::Value const& other) const {{
                return value <= other;
            }}

            bool {fqn}::operator >=({name}::Value const& other) const {{
                return value >= other;
            }}

            bool {fqn}::operator ==({name}::Value const& other) const {{
                return value == other;
            }}

            bool {fqn}::operator !=({name}::Value const& other) const {{
                return value != other;
            }}

            {fqn}::operator Value() const {{
                return value;
            }}

            {fqn}::operator int() const {{
                return value;
            }}

            {fqn}::operator std::string() const {{
                switch(value) {{
            {switches}
                    default:
                        throw std::runtime_error("enum {name}'s value is corrupt, unknown value stored");
                }}
            }}

            {fqn}::operator {protobuf_name}() const {{
                return static_cast<{protobuf_name}>(value);
            }}

            std::ostream& {namespace}::operator<< (std::ostream& out, const {fqn}& val) {{
                return out << static_cast<std::string>(val);
            }}""")

        python_template = dedent("""\
            // Local scope for this enum
            {{
                auto enumclass = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}")
                    .def(pybind11::init<>())
                    .def(pybind11::init<int const&>())
                    .def(pybind11::init<{fqn}::Value const&>())
                    .def(pybind11::init<std::string const&>())
                    .def(pybind11::self < pybind11::self)
                    .def(pybind11::self > pybind11::self)
                    .def(pybind11::self <= pybind11::self)
                    .def(pybind11::self >= pybind11::self)
                    .def(pybind11::self == pybind11::self)
                    .def(pybind11::self < {fqn}::Value())
                    .def(pybind11::self > {fqn}::Value())
                    .def(pybind11::self <= {fqn}::Value())
                    .def(pybind11::self >= {fqn}::Value())
                    .def(pybind11::self == {fqn}::Value())
                    .def_static("include_path", [] {{
                        return "{include_path}";
                    }})
                    .def("_emit", [] ({fqn}& msg, pybind11::capsule capsule) {{
                        // Extract our reactor from the capsule
                        NUClear::Reactor* reactor = capsule;

                        // Do the emit
                        reactor->powerplant.emit_shared<NUClear::dsl::word::emit::Local>(msg.shared_from_this());
                    }});

                pybind11::enum_<{fqn}::Value>(enumclass, "Value")
            {value_list}
                    .export_values();
            }}""")

        return header_template.format(
            name=self.name,
            protobuf_name='::'.join(('.protobuf' + self.fqn).split('.')),
            values=values
        ), impl_template.format(
            fqn='::'.join(self.fqn.split('.')),
            namespace='::'.join(self.package.split('.')),
            name=self.name,
            protobuf_name='::'.join(('.protobuf' + self.fqn).split('.')),
            default_value=default_value,
            if_chain=if_chain,
            switches=switches,
            scope_name=scope_name,
        ), python_template.format(
            fqn='::'.join(self.fqn.split('.')),
            name=self.name,
            include_path=self.include_path,
            value_list=indent('\n'.join('.value("{name}", {fqn}::{name})'.format(name=v[0], fqn=self.fqn.replace('.', '::')) for v in self.values), 8)
        )
Ejemplo n.º 4
0
    def generate_cpp(self):

        header_template = dedent("""\
            class {oneof_name} : public enable_shared_from_this {{
            public:
                {oneof_name}() : {initialiser_list}, val_index(0) {{}}

                template <typename T, size_t index>
                class Proxy {{
                public:
                    Proxy({oneof_name}* oneof) : oneof(oneof) {{}}
                    template <typename U = T>
                    Proxy& operator=(U&& t) {{
                        oneof->data      = std::make_shared<T>(std::forward<U>(t));
                        oneof->val_index = index;
                        return *this;
                    }}
                    bool operator==(const Proxy<T, index>& other) const {{ return get() == other.get(); }}
                    operator T&() {{ return get(); }}
                    operator const T&() const {{ return get(); }}

                    const T& get() const {{
                        if (oneof->val_index == index) {{
                            return *std::static_pointer_cast<T>(oneof->data);
                        }}

                        throw std::range_error("This is not the one of you were looking for.");
                    }}

                    T& get() {{
                        if (oneof->val_index == index) {{
                            return *std::static_pointer_cast<T>(oneof->data);
                        }}

                        throw std::range_error("This is not the one of you were looking for.");
                    }}

                private:
                    {oneof_name}* oneof;
                }};

                bool operator==(const {oneof_name}& other) const {{
                    if (val_index == other.val_index) {{
                        switch (val_index) {{
            {cases}
                            default: return false;
                        }}
                    }}
                    return false;
                }}

                void reset() {{
                    val_index = 0;
                    data.reset();
                }}

            {member_list}

                size_t val_index;

            private:
                std::shared_ptr<void> data;
            }};
            """)

        impl_template = dedent("""\
                        """)

        python_template = dedent("""\
            // Local scope for this message
            {{
                // Use our context and assign a new one to a shadow
                auto shadow = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}");

                // Shadow our context with our new context and declare our subclasses
                auto& context = shadow;

                // Declare the functions on our class (which may use the ones in the subclasses)
                context
                {bindings}
            }}""")

        binding_template = dedent("""\
            context.def_property("{field_name}",
                [](const {fqn}& oneof) -> const {type_name}& {{
                    return oneof.{field_name};
                }},
                [](const {fqn}& oneof, const {type_name}& value) -> void {{
                    oneof.{field_name} = value;
                }});
            """)

        python_bindings = [
            binding_template.format(field_name=v.name,
                                    type_name=v.cpp_type,
                                    fqn=self.fqn.replace('.', '::'))
            for v in self.fields
        ]

        member_list = [
            'Proxy<{}, {}> {};'.format(v.cpp_type, v.number, v.name)
            for v in self.fields
        ]
        initialiser_list = ['{}(this)'.format(v.name) for v in self.fields]
        cases = [
            'case {0}: return {1} == other.{1};'.format(v.number, v.name)
            for v in self.fields
        ]

        return (header_template.format(
            oneof_name=self.name,
            member_list=indent('\n'.join(member_list), 4),
            initialiser_list=', '.join(initialiser_list),
            cases=indent('\n'.join(cases), 16)), impl_template,
                python_template.format(bindings='\n\n'.join(python_bindings),
                                       fqn=self.fqn.replace('.', '::'),
                                       name=self.name))
Ejemplo n.º 5
0
    def generate_cpp(self):

        # Make our value pairs
        values = indent('\n'.join(['{} = {}'.format(v[0], v[1]) for v in self.values]), 8)
        values = ',\n'.join([v for v in values.splitlines()])

        # Make our switch statement pairs
        switches = indent('\n'.join(['case Value::{}: return "{}";'.format(v[0], v[0]) for v in self.values]), 8)

        # Make our if chain
        if_chain = indent('\n'.join(['if (str == "{}") value = Value::{};'.format(v[0], v[0]) for v in self.values]))

        # Get our default value
        default_value = dict([reversed(v) for v in self.values])[0]

        # Make our fancy enums
        header_template = dedent("""\
            struct {name} : public ::message::MessageBase<{name}> {{
                enum Value {{
            {values}
                }};
                Value value;

                // Constructors
                {name}();

                {name}(int const& v);

                {name}(Value const& value);

                {name}(std::string const& str);

                {name}({protobuf_name} const& p);

                // Operators
                bool operator <({name} const& other) const;

                bool operator >({name} const& other) const;

                bool operator <=({name} const& other) const;

                bool operator >=({name} const& other) const;

                bool operator ==({name} const& other) const;

                bool operator <({name}::Value const& other) const;

                bool operator >({name}::Value const& other) const;

                bool operator <=({name}::Value const& other) const;

                bool operator >=({name}::Value const& other) const;

                bool operator ==({name}::Value const& other) const;

                // Conversions
                operator Value() const;

                operator int() const;

                operator std::string() const;

                operator {protobuf_name}() const;
            }};""")

        impl_template = dedent("""\
            {fqn}::{name}() : value(Value::{default_value}) {{}}

            {fqn}::{name}(int const& v) : value(static_cast<Value>(v)) {{}}

            {fqn}::{name}(Value const& value) : value(value) {{}}

            {fqn}::{name}(std::string const& str) {{
            {if_chain}
                throw std::runtime_error("String did not match any enum for {name}");
            }}

            {fqn}::{name}({protobuf_name} const& p) {{
                value = static_cast<Value>(p);
            }}

            bool {fqn}::operator <({name} const& other) const {{
                return value < other.value;
            }}

            bool {fqn}::operator >({name} const& other) const {{
                return value > other.value;
            }}

            bool {fqn}::operator <=({name} const& other) const {{
                return value <= other.value;
            }}

            bool {fqn}::operator >=({name} const& other) const {{
                return value >= other.value;
            }}

            bool {fqn}::operator ==({name} const& other) const {{
                return value == other.value;
            }}

            bool {fqn}::operator <({name}::Value const& other) const {{
                return value < other;
            }}

            bool {fqn}::operator >({name}::Value const& other) const {{
                return value > other;
            }}

            bool {fqn}::operator <=({name}::Value const& other) const {{
                return value <= other;
            }}

            bool {fqn}::operator >=({name}::Value const& other) const {{
                return value >= other;
            }}

            bool {fqn}::operator ==({name}::Value const& other) const {{
                return value == other;
            }}

            {fqn}::operator Value() const {{
                return value;
            }}

            {fqn}::operator int() const {{
                return value;
            }}

            {fqn}::operator std::string() const {{
                switch(value) {{
            {switches}
                    default:
                        throw std::runtime_error("enum {name}'s value is corrupt, unknown value stored");
                }}
            }}

            {fqn}::operator {protobuf_name}() const {{
                return static_cast<{protobuf_name}>(value);
            }}""")

        python_template = dedent("""\
            // Local scope for this enum
            {{
                auto enumclass = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}")
                    .def(pybind11::init<>())
                    .def(pybind11::init<int const&>())
                    .def(pybind11::init<{fqn}::Value const&>())
                    .def(pybind11::init<std::string const&>())
                    .def(pybind11::self < pybind11::self)
                    .def(pybind11::self > pybind11::self)
                    .def(pybind11::self <= pybind11::self)
                    .def(pybind11::self >= pybind11::self)
                    .def(pybind11::self == pybind11::self)
                    .def(pybind11::self < {fqn}::Value())
                    .def(pybind11::self > {fqn}::Value())
                    .def(pybind11::self <= {fqn}::Value())
                    .def(pybind11::self >= {fqn}::Value())
                    .def(pybind11::self == {fqn}::Value())
                    .def_static("include_path", [] {{
                        return "{include_path}";
                    }});

                pybind11::enum_<{fqn}::Value>(enumclass, "Value")
            {value_list}
                    .export_values();
            }}""")

        return header_template.format(
            name=self.name,
            protobuf_name='::'.join(('.protobuf' + self.fqn).split('.')),
            values=values
        ), impl_template.format(
            fqn='::'.join(self.fqn.split('.')),
            name=self.name,
            protobuf_name='::'.join(('.protobuf' + self.fqn).split('.')),
            default_value=default_value,
            if_chain=if_chain,
            switches=switches
        ), python_template.format(
            fqn='::'.join(self.fqn.split('.')),
            name=self.name,
            include_path=self.include_path,
            value_list=indent('\n'.join('.value("{name}", {fqn}::{name})'.format(name=v[0], fqn=self.fqn.replace('.', '::')) for v in self.values), 8)
        )
Ejemplo n.º 6
0
    def generate_protobuf_constructor(self):

        # Fully qualified c++ name
        cpp_fqn = '::'.join(self.fqn.split('.'))

        # Protobuf name
        protobuf_name = '::'.join(('.protobuf' + self.fqn).split('.'))

        # If we are empty it's easy
        if not self.fields:
            return (
                '{}(const {}&);'.format(self.name, protobuf_name),
                '{}::{}(const {}&) {{}}'.format(cpp_fqn, self.name, protobuf_name)
            )
        else:
            lines = ['{}::{}(const {}& proto) {{'.format(cpp_fqn, self.name, protobuf_name)]

            for v in self.fields:
                if v.pointer:
                    print('TODO HANDLE POINTER CASES')

                elif v.map_type:
                    if v.type[1].bytes_type:
                        lines.append(indent('for (auto& _v : proto.{}()) {{'.format(v.name)))
                        lines.append(
                            indent(
                                '{0}[_v.first].insert(std::end({0}[_v.first]), std::begin(_v.second), std::end(_v.second));'.
                                format(v.name), 8
                            )
                        )
                        lines.append(indent('}'))

                    elif v.type[1].special_cpp_type:
                        lines.append(indent('for (auto& _v : proto.{}()) {{'.format(v.name.lower())))
                        lines.append(indent('message::conversion::convert({}[_v.first], _v.second);'.format(v.name), 8))
                        lines.append(indent('}'))

                    else:  # Basic and other types are handled the same
                        lines.append(
                            indent(
                                '{0}.insert(std::begin(proto.{1}()), std::end(proto.{1}()));'.format(
                                    v.name, v.name.lower()
                                ), 8
                            )
                        )

                elif v.repeated:
                    if v.bytes_type:
                        lines.append(indent('{0}.resize(proto.{0}_size());'.format(v.name.lower())))
                        lines.append(indent('for (size_t _i = 0; _i < {0}.size(); ++_i) {{'.format(v.name)))
                        lines.append(
                            indent(
                                '{0}[_i].insert(std::end({0}[_i]), std::begin(proto.{1}(_i)), std::end(proto.{1}(_i)));'.
                                format(v.name, v.name.lower()), 8
                            )
                        )
                        lines.append(indent('}'))

                    elif v.special_cpp_type:
                        if v.array_size > 0:
                            lines.append(
                                indent(
                                    'for (size_t _i = 0; _i < {0}.size() && _i < size_t(proto.{1}_size()); ++_i) {{'.
                                    format(v.name, v.name.lower())
                                )
                            )
                            lines.append(
                                indent(
                                    'message::conversion::convert({0}[_i], proto.{1}(_i));'.format(
                                        v.name, v.name.lower()
                                    ), 8
                                )
                            )
                            lines.append(indent('}'))
                        else:
                            # Add the top of our for loop for the repeated field
                            lines.append(indent('{0}.resize(proto.{1}_size());'.format(v.name, v.name.lower())))
                            lines.append(indent('for (size_t _i = 0; _i < {0}.size(); ++_i) {{'.format(v.name)))
                            lines.append(
                                indent(
                                    'message::conversion::convert({0}[_i], proto.{1}(_i));'.format(
                                        v.name, v.name.lower()
                                    ), 8
                                )
                            )
                            lines.append(indent('}'))

                    else:  # Basic and other types are handled the same
                        if v.array_size > 0:
                            lines.append(
                                indent(
                                    'for (size_t _i = 0; _i < {0}.size() && _i < size_t(proto.{1}_size()); ++_i) {{'.
                                    format(v.name, v.name.lower())
                                )
                            )
                            lines.append(indent('{0}[_i] = proto.{1}(_i);'.format(v.name, v.name.lower()), 8))
                            lines.append(indent('}'))
                        else:
                            lines.append(
                                indent(
                                    '{0}.insert(std::end({0}), std::begin(proto.{1}()), std::end(proto.{1}()));'.format(
                                        v.name, v.name.lower()
                                    )
                                )
                            )

                elif v.one_of:
                    lines.append(indent('switch (proto.{}_case()) {{').format(v.name.lower()))
                    for oneof_field in v.oneof_fields:
                        lines.append(indent('case {}: {{'.format(oneof_field.number), 8))

                        if oneof_field.bytes_type:
                            lines.append(
                                indent(
                                    dedent(
                                        """\
                                        {0}.{1} = {2};
                                        {0}.{1}.insert(std::end({0}.{1}), std::begin(proto.{3}()), std::end(proto.{3}()));"""
                                        .format(
                                            v.name, oneof_field.name, oneof_field.default_value,
                                            oneof_field.name.lower()
                                        )
                                    ), 12
                                )
                            )

                        elif oneof_field.special_cpp_type:
                            lines.append(
                                indent(
                                    dedent(
                                        """\
                                        {0}.{1} = {2};
                                        message::conversion::convert({0}.{1}, proto.{3}());""".format(
                                            v.name, oneof_field.name, oneof_field.default_value,
                                            oneof_field.name.lower()
                                        )
                                    ), 12
                                )
                            )

                        else:  # Basic and other types are handled the same
                            lines.append(
                                indent(
                                    '{0}.{1} = proto.{2}();'.format(v.name, oneof_field.name, oneof_field.name.lower()),
                                    12
                                )
                            )

                        lines.append(indent('} break;', 8))
                    lines.append(indent('default: object.reset(); break;', 8))
                    lines.append(indent('}'))

                else:
                    if v.bytes_type:
                        lines.append(
                            indent(
                                '{0}.insert(std::end({0}), std::begin(proto.{1}()), std::end(proto.{1}()));'.format(
                                    v.name, v.name.lower()
                                )
                            )
                        )

                    elif v.special_cpp_type:
                        lines.append(
                            indent('message::conversion::convert({}, proto.{}());'.format(v.name, v.name.lower()))
                        )

                    else:  # Basic and other types are handled the same
                        lines.append(indent('{} = proto.{}();'.format(v.name, v.name.lower())))

            lines.append('}')

            return '{}(const {}& proto);'.format(self.name, protobuf_name), '\n'.join(lines)
Ejemplo n.º 7
0
    def generate_cpp(self):

        # Make our value pairs
        fields = indent('\n'.join(['{}'.format(v.generate_cpp_header()) for v in self.fields]))

        # Generate our protobuf class name
        protobuf_type = '::'.join(('.protobuf' + self.fqn).split('.'))

        # Generate our enums c++
        enums = [e.generate_cpp() for e in self.enums]
        enum_headers = indent('\n\n'.join([e[0] for e in enums]))
        enum_impls = ('\n\n'.join([e[1] for e in enums]))
        enum_python = ('\n\n'.join([e[2] for e in enums]))

        # Generate our submessage c++
        submessages = [s.generate_cpp() for s in self.submessages]
        submessage_headers = indent('\n\n'.join([s[0] for s in submessages]))
        submessage_impls = ('\n\n'.join([s[1] for s in submessages]))
        submessage_python = ('\n\n'.join([s[2] for s in submessages]))

        # Get our function code
        default_constructor = self.generate_default_constructor()
        rule_of_five = self.generate_rule_of_five()
        protobuf_constructor = self.generate_protobuf_constructor()
        protobuf_converter = self.generate_protobuf_converter()
        equality_operator = self.generate_equality_operator()

        constructor_headers = indent(
            '\n\n'.join([default_constructor[0], rule_of_five[0], protobuf_constructor[0], equality_operator[0]])
        )
        constructor_impl = '\n\n'.join([default_constructor[1], protobuf_constructor[1], equality_operator[1]])
        converter_headers = indent('\n\n'.join([protobuf_converter[0]]))
        converter_impl = '\n\n'.join([protobuf_converter[1]])

        header_template = dedent(
            """\
            struct {name} : public ::message::MessageBase<{name}> {{
                // Protobuf type
                using protobuf_type = {protobuf_type};

                // Enum Definitions
            {enums}
                // Submessage Definitions
            {submessages}
                // Constructors
            {constructors}
                // Converters
            {converters}
                // Fields
            {fields}
            }};"""
        )

        impl_template = dedent(
            """\
            // Constructors
            {constructors}

            // Converters
            {converters}

            // Subenums
            {enums}

            // Submessages
            {submessages}"""
        )

        python_template = dedent(
            """\
            // Local scope for this message
            {{
                // Use our context and assign a new one to a shadow
                auto shadow = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}");

                // Shadow our context with our new context and declare our subclasses
                auto& context = shadow;

            {enums}

            {submessages}

                // Declare the functions on our class (which may use the ones in the subclasses)
                context
            {constructor}
            {message_members};
                context.def_static("include_path", [] {{
                    return "{include_path}";
                }});

                // Build our emitter function that is used to emit this object
                context.def("_emit", [] ({fqn}& msg, pybind11::capsule capsule) {{
                    // Extract our reactor from the capsule
                    NUClear::Reactor* reactor = capsule;

                    // Do the emit
                    reactor->powerplant.emit_shared<NUClear::dsl::word::emit::Local>(msg.shared_from_this());
                }});
            }}"""
        )

        python_constructor_args = ['{}& self'.format(self.fqn.replace('.', '::'))]
        python_constructor_args.extend(['{} const& _{}'.format(t.cpp_type, t.name) for t in self.fields])
        python_members = '\n'.join(
            '.def_readwrite("{field}", &{fqn}::{field})'.format(field=f.name, fqn=self.fqn.replace('.', '::'))
            for f in self.fields
        )
        python_constructor_default_args = ['']
        python_constructor_default_args.extend([
            'pybind11::arg("{}") = {}'.format(
                t.name, t.default_value if t.default_value else '{}()'.format(t.cpp_type)
            ) for t in self.fields
        ])

        python_constructor = dedent(
            """\
            .def("__init__", [] ({args}) {{
                new (&self) {name}({vars});
            }}{default_args})"""
        ).format(
            name=self.fqn.replace('.', '::'),
            args=', '.join(python_constructor_args),
            vars=', '.join('_{}'.format(t.name) for t in self.fields),
            default_args=', '.join(python_constructor_default_args)
        )

        return header_template.format(
            name=self.name,
            enums=enum_headers,
            submessages=submessage_headers,
            constructors=constructor_headers,
            protobuf_type=protobuf_type,
            converters=converter_headers,
            fields=fields
        ), impl_template.format(
            constructors=constructor_impl, converters=converter_impl, enums=enum_impls, submessages=submessage_impls
        ), python_template.format(
            constructor=indent(python_constructor, 8),
            message_members=indent(python_members, 8),
            include_path=self.include_path,
            fqn=self.fqn.replace('.', '::'),
            name=self.name,
            submessages=indent(submessage_python),
            enums=indent(enum_python)
        )
Ejemplo n.º 8
0
    def generate_cpp(self):

        # Make our value pairs
        fields = indent('\n'.join(['{}'.format(v.generate_cpp_header()) for v in self.fields]))

        # Generate our protobuf class name
        protobuf_type = '::'.join(('.protobuf' + self.fqn).split('.'))

        # Generate our enums c++
        enums = [e.generate_cpp() for e in self.enums]
        enum_headers = indent('\n\n'.join([e[0] for e in enums]))
        enum_impls = ('\n\n'.join([e[1] for e in enums]))
        enum_python = ('\n\n'.join([e[2] for e in enums]))

        # Generate our submessage c++
        submessages = [s.generate_cpp() for s in self.submessages]
        submessage_headers = indent('\n\n'.join([s[0] for s in submessages]))
        submessage_impls = ('\n\n'.join([s[1] for s in submessages]))
        submessage_python = ('\n\n'.join([s[2] for s in submessages]))

        # Get our function code
        default_constructor = self.generate_default_constructor()
        protobuf_constructor = self.generate_protobuf_constructor()
        protobuf_converter = self.generate_protobuf_converter()

        constructor_headers = indent('\n\n'.join([default_constructor[0], protobuf_constructor[0]]))
        constructor_impl = '\n\n'.join([default_constructor[1], protobuf_constructor[1]])
        converter_headers = indent('\n\n'.join([protobuf_converter[0]]))
        converter_impl = '\n\n'.join([protobuf_converter[1]])

        header_template = dedent("""\
            struct alignas(16) {name} : public ::message::MessageBase<{name}> {{
                // Protobuf type
                using protobuf_type = {protobuf_type};

                // Enum Definitions
            {enums}
                // Submessage Definitions
            {submessages}
                // Constructors
            {constructors}
                // Converters
            {converters}
                // Fields
            {fields}
            }};""")

        impl_template = dedent("""\
            // Constructors
            {constructors}

            // Converters
            {converters}

            // Subenums
            {enums}

            // Submessages
            {submessages}""")

        python_template = dedent("""\
            // Local scope for this message
            {{
                // Use our context and assign a new one to a shadow
                auto shadow = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}");

                // Shadow our context with our new context and declare our subclasses
                auto& context = shadow;
            {submessages}
            {enums}

                // Declare the functions on our class (which may use the ones in the subclasses)
                context
            {constructor}
            {message_members};
                context.def_static("include_path", [] {{
                    return "{include_path}";
                }});
            }}""")

        python_members = '\n'.join('.def_readwrite("{field}", &{fqn}::{field})'.format(field=f.name, fqn=self.fqn.replace('.', '::')) for f in self.fields)
        python_constructor = dedent("""\
            .def("__init__", [] ({name}& self, {args}) {{
                new (&self) {name}({vars});
            }}, {default_args})""").format(
            name=self.fqn.replace('.', '::'),
            args=', '.join('{} const& {}'.format(t.cpp_type, t.name) for t in self.fields),
            vars=', '.join(t.name for t in self.fields),
            default_args=', '.join('pybind11::arg("{}") = {}'.format(t.name, t.default_value if t.default_value else '{}()'.format(t.cpp_type)) for t in self.fields)
        )

        return header_template.format(
            name=self.name,
            enums=enum_headers,
            submessages=submessage_headers,
            constructors=constructor_headers,
            protobuf_type=protobuf_type,
            converters=converter_headers,
            fields=fields
        ), impl_template.format(
            constructors=constructor_impl,
            converters=converter_impl,
            enums=enum_impls,
            submessages=submessage_impls
        ), python_template.format(
            constructor=indent(python_constructor, 8),
            message_members=indent(python_members, 8),
            include_path=self.include_path,
            fqn=self.fqn.replace('.', '::'),
            name=self.name,
            submessages=indent(submessage_python),
            enums=indent(enum_python)
        )
Ejemplo n.º 9
0
    def generate_cpp(self):

        define = '{}_H'.format('_'.join([s.upper() for s in self.name[:-6].strip().split('/')]))
        parts = self.package.split('.')
        ns_open = '\n'.join(['namespace {} {{'.format(x) for x in parts])
        ns_close = '\n'.join('}' * len(parts))

        # Generate our enums c++
        enums = [e.generate_cpp() for e in self.enums]
        enum_headers = indent('\n\n'.join([e[0] for e in enums]))
        enum_impls = ('\n\n'.join([e[1] for e in enums]))
        enum_python = ('\n\n'.join([e[2] for e in enums]))

        # Generate our enums c++
        messages = [m.generate_cpp() for m in self.messages]
        message_headers = indent('\n\n'.join([m[0] for m in messages]))
        message_impls = ('\n\n'.join([m[1] for m in messages]))
        message_python = ('\n\n'.join([m[2] for m in messages]))

        # By default include some useful headers
        includes = {
            '1<cstdint>',
            '2<string>',
            '2<map>',
            '2<vector>',
            '2<array>',
            '2<memory>',
            '4"{}"'.format(self.name[:-6] + '.pb.h'),
            '5"message/MessageBase.h"'
        }

        # We use a dirty hack here of putting a priority on each header
        # to make the includes be in a better order
        for d in self.dependencies:
            if d in ['Vector.proto', 'Matrix.proto']:
                includes.add('4"message/conversion/proto_matrix.h"')
            elif d in ['EnhancedMessage.proto']:
                pass # We don't need to do anything for these ones
            elif d in ['Transform.proto']:
                includes.add('4"message/conversion/proto_transform.h"')
            elif d in ['google/protobuf/timestamp.proto', 'google/protobuf/duration.proto']:
                includes.add('4"message/conversion/proto_time.h"')
            elif d in ['google/protobuf/struct.proto']:
                includes.add('4"utility/include/proto_struct.h"')
            else:
                includes.add('4"{}"'.format(d[:-6] + '.h'))

        # Don't forget to remove the first character
        includes = '\n'.join(['#include {}'.format(i[1:]) for i in sorted(list(includes))])

        header_template = dedent("""\
            #ifndef {define}
            #define {define}

            {includes}

            {openNamespace}

                // Enum Definitions
            {enums}
                // Message Definitions
            {messages}

            {closeNamespace}

            #endif  // {define}
            """)

        impl_template = dedent("""\
            {include}

            // Enum Implementations
            {enums}

            // Message Implementations
            {messages}
            """)

        python_template = dedent("""\
            #include <pybind11/pybind11.h>
            #include <pybind11/complex.h>
            #include <pybind11/stl.h>
            #include <pybind11/chrono.h>
            #include <pybind11/operators.h>
            #include <pybind11/eigen.h>

            {include}

            void init_{filename}(pybind11::module& module) {{

                // Go down to our submodule as required as context
                pybind11::module context = module{submodules};

            {messages}

            {enums}
            }}
            """)

        python_submodules = ''.join('.def_submodule("{}")'.format(m) for m in self.fqn.split('.')[2:])

        return header_template.format(
            define=define,
            includes=includes,
            openNamespace=ns_open,
            enums=enum_headers,
            messages=message_headers,
            closeNamespace=ns_close
        ), impl_template.format(
            include='#include "{}"'.format(self.name[:-6] + '.h'),
            enums=enum_impls,
            messages=message_impls
        ), python_template.format(
            include='#include "{}"'.format(self.name[:-6] + '.h'),
            messages=indent(message_python),
            enums=indent(enum_python),
            filename=re.sub(r'[^A-Za-z0-9]', '_', self.name),
            submodules=python_submodules
        )
Ejemplo n.º 10
0
    def generate_cpp(self):

        header_template = dedent(
            """\
            class {oneof_name} : public enable_shared_from_this {{
            public:
                {oneof_name}() : {initialiser_list}, val_index(0) {{}}

                template <typename T, size_t index>
                class Proxy {{
                public:
                    Proxy({oneof_name}* oneof) : oneof(oneof) {{}}
                    template <typename U = T>
                    Proxy& operator=(U&& t) {{
                        oneof->data      = std::make_shared<T>(std::forward<U>(t));
                        oneof->val_index = index;
                        return *this;
                    }}
                    bool operator==(const Proxy<T, index>& other) const {{ return get() == other.get(); }}
                    operator T&() {{ return get(); }}
                    operator const T&() const {{ return get(); }}

                    const T& get() const {{
                        if (oneof->val_index == index) {{
                            return *std::static_pointer_cast<T>(oneof->data);
                        }}

                        throw std::range_error("This is not the one of you were looking for.");
                    }}

                    T& get() {{
                        if (oneof->val_index == index) {{
                            return *std::static_pointer_cast<T>(oneof->data);
                        }}

                        throw std::range_error("This is not the one of you were looking for.");
                    }}

                private:
                    {oneof_name}* oneof;
                }};

                bool operator==(const {oneof_name}& other) const {{
                    if (val_index == other.val_index) {{
                        switch (val_index) {{
            {cases}
                            default: return false;
                        }}
                    }}
                    return false;
                }}

                void reset() {{
                    val_index = 0;
                    data.reset();
                }}

            {member_list}

                size_t val_index;

            private:
                std::shared_ptr<void> data;
            }};
            """
        )

        impl_template = dedent("""\
                        """)

        python_template = dedent(
            """\
            // Local scope for this message
            {{
                // Use our context and assign a new one to a shadow
                auto shadow = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}");

                // Shadow our context with our new context and declare our subclasses
                auto& context = shadow;

                // Declare the functions on our class (which may use the ones in the subclasses)
                context
                {bindings}
            }}"""
        )

        binding_template = dedent(
            """\
            context.def_property("{field_name}",
                [](const {fqn}& oneof) -> const {type_name}& {{
                    return oneof.{field_name};
                }},
                [](const {fqn}& oneof, const {type_name}& value) -> void {{
                    oneof.{field_name} = value;
                }});
            """
        )

        python_bindings = [
            binding_template.format(field_name=v.name, type_name=v.cpp_type, fqn=self.fqn.replace('.', '::'))
            for v in self.fields
        ]

        member_list = ['Proxy<{}, {}> {};'.format(v.cpp_type, v.number, v.name) for v in self.fields]
        initialiser_list = ['{}(this)'.format(v.name) for v in self.fields]
        cases = ['case {0}: return {1} == other.{1};'.format(v.number, v.name) for v in self.fields]

        return (
            header_template.format(
                oneof_name=self.name,
                member_list=indent('\n'.join(member_list), 4),
                initialiser_list=', '.join(initialiser_list),
                cases=indent('\n'.join(cases), 16)
            ), impl_template,
            python_template.format(
                bindings='\n\n'.join(python_bindings), fqn=self.fqn.replace('.', '::'), name=self.name
            )
        )
Ejemplo n.º 11
0
    def generate_cpp(self):

        define = "{}_H".format("_".join(
            [s.upper() for s in self.name[:-6].strip().split("/")]))
        parts = self.package.split(".")
        ns_open = "\n".join(["namespace {} {{".format(x) for x in parts])
        ns_close = "\n".join("}" * len(parts))

        # Generate our enums c++
        enums = [e.generate_cpp() for e in self.enums]
        enum_headers = indent("\n\n".join([e[0] for e in enums]))
        enum_impls = "\n\n".join([e[1] for e in enums])
        enum_python = "\n\n".join([e[2] for e in enums])

        # Generate our enums c++
        messages = [m.generate_cpp() for m in self.messages]
        message_headers = indent("\n\n".join([m[0] for m in messages]))
        message_impls = "\n\n".join([m[1] for m in messages])
        message_python = "\n\n".join([m[2] for m in messages])

        # By default include some useful headers
        # yapf: disable
        includes = {
            '1<cstdint>',
            '2<string>',
            '2<array>',
            '2<exception>',
            '2<map>',
            '2<memory>',
            '2<vector>',
            '4"{}"'.format(self.name[:-6] + '.pb.h'),
            '5"message/MessageBase.h"'
        }
        # yapf: enable

        # We use a dirty hack here of putting a priority on each header
        # to make the includes be in a better order
        for d in self.dependencies:
            if d in ["Vector.proto", "Matrix.proto"]:
                includes.add('4"message/conversion/proto_matrix.h"')
            elif d in ["Neutron.proto"]:
                pass  # We don't need to do anything for these ones
            elif d in [
                    "google/protobuf/timestamp.proto",
                    "google/protobuf/duration.proto"
            ]:
                includes.add('4"message/conversion/proto_time.h"')
            else:
                includes.add('4"{}"'.format(d[:-6] + ".h"))

        # Don't forget to remove the first character
        includes = "\n".join(
            ["#include {}".format(i[1:]) for i in sorted(list(includes))])

        header_template = dedent("""\
            #ifndef {define}
            #define {define}

            {includes}

            {open_namespace}

                // Enum Definitions
            {enums}
                // Message Definitions
            {messages}

            {close_namespace}

            #endif  // {define}
            """)

        impl_template = dedent("""\
            {include}

            // Enum Implementations
            {enums}

            // Message Implementations
            {messages}
            """)

        python_template = dedent("""\
            #include <pybind11/pybind11.h>
            #include <pybind11/complex.h>
            #include <pybind11/stl.h>
            #include <pybind11/chrono.h>
            #include <pybind11/operators.h>
            #include <pybind11/eigen.h>

            {include}

            void init_{filename}(pybind11::module& module) {{

                // Go down to our submodule as required as context
                pybind11::module context = module{submodules};

            {enums}

            {messages}
            }}
            """)

        python_submodules = "".join('.def_submodule("{}")'.format(m)
                                    for m in self.fqn.split(".")[2:])

        return (
            header_template.format(
                define=define,
                includes=includes,
                open_namespace=ns_open,
                enums=enum_headers,
                messages=message_headers,
                close_namespace=ns_close,
            ),
            impl_template.format(
                include='#include "{}"'.format(self.name[:-6] + ".h"),
                enums=enum_impls,
                messages=message_impls),
            python_template.format(
                include='#include "{}"'.format(self.name[:-6] + ".h"),
                messages=indent(message_python),
                enums=indent(enum_python),
                filename=re.sub(r"[^A-Za-z0-9]", "_", self.name),
                submodules=python_submodules,
            ),
        )
Ejemplo n.º 12
0
    def generate_cpp(self):

        # Make our value pairs
        values = indent(
            '\n'.join(['{} = {}'.format(v[0], v[1]) for v in self.values]), 8)
        values = ',\n'.join([v for v in values.splitlines()])

        scope_name = '_'.join(self.fqn.split('.'))

        # Make our switch statement pairs
        switches = indent(
            '\n'.join([
                'case Value::{}: return "{}";'.format(v[0], v[0])
                for v in self.values
            ]), 8)

        # Make our if chain
        if_chain = indent('\nelse '.join([
            'if (str == "{}") value = Value::{};'.format(v[0], v[0])
            for v in self.values
        ]))

        # Get our default value
        default_value = dict([reversed(v) for v in self.values])[0]

        # Make our fancy enums
        header_template = dedent("""\
            struct {name} : public ::message::MessageBase<{name}> {{
                enum Value {{
            {values}
                }};
                Value value;

                // Constructors
                {name}();

                {name}(int const& v);

                {name}(Value const& value);

                {name}(std::string const& str);

                {name}({protobuf_name} const& p);

                // Operators
                bool operator <({name} const& other) const;

                bool operator >({name} const& other) const;

                bool operator <=({name} const& other) const;

                bool operator >=({name} const& other) const;

                bool operator ==({name} const& other) const;

                bool operator !=({name} const& other) const;

                bool operator <({name}::Value const& other) const;

                bool operator >({name}::Value const& other) const;

                bool operator <=({name}::Value const& other) const;

                bool operator >=({name}::Value const& other) const;

                bool operator ==({name}::Value const& other) const;

                bool operator !=({name}::Value const& other) const;

                // Conversions
                operator Value() const;

                operator int() const;

                operator std::string() const;

                operator {protobuf_name}() const;

                friend std::ostream& operator<< (std::ostream& out, const {name}& val);
            }};""")

        impl_template = dedent("""\
            typedef {fqn} T{scope_name};

            {fqn}::{name}() : value(Value::{default_value}) {{}}

            {fqn}::{name}(int const& v) : value(static_cast<Value>(v)) {{}}

            {fqn}::{name}(Value const& value) : value(value) {{}}

            {fqn}::{name}(std::string const& str) {{
            {if_chain}
                else throw std::runtime_error("String " + str + " did not match any enum for {name}");
            }}

            {fqn}::{name}({protobuf_name} const& p) {{
                value = static_cast<Value>(p);
            }}

            bool {fqn}::operator <({name} const& other) const {{
                return value < other.value;
            }}

            bool {fqn}::operator >({name} const& other) const {{
                return value > other.value;
            }}

            bool {fqn}::operator <=({name} const& other) const {{
                return value <= other.value;
            }}

            bool {fqn}::operator >=({name} const& other) const {{
                return value >= other.value;
            }}

            bool {fqn}::operator ==({name} const& other) const {{
                return value == other.value;
            }}

            bool {fqn}::operator !=({name} const& other) const {{
                return value != other.value;
            }}

            bool {fqn}::operator <({name}::Value const& other) const {{
                return value < other;
            }}

            bool {fqn}::operator >({name}::Value const& other) const {{
                return value > other;
            }}

            bool {fqn}::operator <=({name}::Value const& other) const {{
                return value <= other;
            }}

            bool {fqn}::operator >=({name}::Value const& other) const {{
                return value >= other;
            }}

            bool {fqn}::operator ==({name}::Value const& other) const {{
                return value == other;
            }}

            bool {fqn}::operator !=({name}::Value const& other) const {{
                return value != other;
            }}

            {fqn}::operator Value() const {{
                return value;
            }}

            {fqn}::operator int() const {{
                return value;
            }}

            {fqn}::operator std::string() const {{
                switch(value) {{
            {switches}
                    default:
                        throw std::runtime_error("enum {name}'s value is corrupt, unknown value stored");
                }}
            }}

            {fqn}::operator {protobuf_name}() const {{
                return static_cast<{protobuf_name}>(value);
            }}

            std::ostream& {namespace}::operator<< (std::ostream& out, const {fqn}& val) {{
                return out << static_cast<std::string>(val);
            }}""")

        python_template = dedent("""\
            // Local scope for this enum
            {{
                auto enumclass = pybind11::class_<{fqn}, std::shared_ptr<{fqn}>>(context, "{name}")
                    .def(pybind11::init<>())
                    .def(pybind11::init<int const&>())
                    .def(pybind11::init<{fqn}::Value const&>())
                    .def(pybind11::init<std::string const&>())
                    .def(pybind11::self < pybind11::self)
                    .def(pybind11::self > pybind11::self)
                    .def(pybind11::self <= pybind11::self)
                    .def(pybind11::self >= pybind11::self)
                    .def(pybind11::self == pybind11::self)
                    .def(pybind11::self < {fqn}::Value())
                    .def(pybind11::self > {fqn}::Value())
                    .def(pybind11::self <= {fqn}::Value())
                    .def(pybind11::self >= {fqn}::Value())
                    .def(pybind11::self == {fqn}::Value())
                    .def_static("include_path", [] {{
                        return "{include_path}";
                    }})
                    .def("_emit", [] ({fqn}& msg, pybind11::capsule capsule) {{
                        // Extract our reactor from the capsule
                        NUClear::Reactor* reactor = capsule;

                        // Do the emit
                        reactor->powerplant.emit_shared<NUClear::dsl::word::emit::Local>(msg.shared_from_this());
                    }});

                pybind11::enum_<{fqn}::Value>(enumclass, "Value")
            {value_list}
                    .export_values();
            }}""")

        return header_template.format(
            name=self.name,
            protobuf_name='::'.join(('.protobuf' + self.fqn).split('.')),
            values=values), impl_template.format(
                fqn='::'.join(self.fqn.split('.')),
                namespace='::'.join(self.package.split('.')),
                name=self.name,
                protobuf_name='::'.join(('.protobuf' + self.fqn).split('.')),
                default_value=default_value,
                if_chain=if_chain,
                switches=switches,
                scope_name=scope_name,
            ), python_template.format(
                fqn='::'.join(self.fqn.split('.')),
                name=self.name,
                include_path=self.include_path,
                value_list=indent(
                    '\n'.join('.value("{name}", {fqn}::{name})'.format(
                        name=v[0], fqn=self.fqn.replace('.', '::'))
                              for v in self.values), 8))