Class: OroGen::Gen::RTT_CPP::Typekit

Inherits:
Object
  • Object
show all
Includes:
OpaqueHandling
Defined in:
lib/orogen/gen/typekit.rb,
lib/orogen/marshallers/ros.rb

Overview

Support for typekit generation in oroGen

Constant Summary collapse

INCLUDE_DIR_NAME =
'types'
TYPE_EXPORT_POLICIES =

The three possible type export policies. See #type_export_policy and #export_types

[:all, :used, :selected]
['bitfield']
BASE_TYPES =
['int', 'unsigned int', 'double', 'float', 'bool', 'char']
BASE_TYPES_RTT_NAMES =
{
'unsigned int' => 'uint' }
BASE_TYPES_NEEDED_TRANSPORTS =
%w{typelib ros}
COPY_METADATA_EXCLUDED_KEYS =
%w{source_file_line cxx_name orogen_include}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from OpaqueHandling

#find_opaque_for_intermediate, #intermediate_type?, #intermediate_type_for, #intermediate_type_name_for, #m_type?, #opaque_specification

Constructor Details

#initialize(project = nil) ⇒ Typekit

Returns a new instance of Typekit



838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
# File 'lib/orogen/gen/typekit.rb', line 838

def initialize(project = nil)
    @project = project

    @include_dirs = Set.new
    @included_files = Array.new

    @plugins = []
    plugins << (TypekitMarshallers::TypeInfo::Plugin.new(self))

    @internal_dependencies = []
    @imports, @loads    = [], []
    @registry           = Typelib::Registry.new
    @imported_typekits  = []
    @imported_types     = Typelib::Registry.new
    @imported_typelist  = Set.new

    @used_libraries        = []
    @linked_used_libraries = []

    @opaque_registry    = Typelib::Registry.new
    @opaques            = Array.new
    @loaded_files_dirs  = Set.new
    @pending_load_options = []
    # The order matters ! GCCXML unfortunately gave as file names
    # the argument to #include. So, if we are unlucky, one file
    # will be loaded recursively and we won't actually detect it
    # ... :( Not sure if this is still relevant with the
    # clang-based importer
    #
    # In other words, keep pending_loads an array
    @pending_loads        = Array.new

    type_export_policy :all
    @selected_types = Set.new
    @excluded_types = Set.new
    Project.using_rtt_typekit(self)
end

Class Attribute Details

.pluginsObject (readonly)

Returns the value of attribute plugins



565
566
567
# File 'lib/orogen/gen/typekit.rb', line 565

def plugins
  @plugins
end

Instance Attribute Details

#automatic_dirObject

The directory in which generated files that are meant to not be modified by the user should be saved



617
618
619
# File 'lib/orogen/gen/typekit.rb', line 617

def automatic_dir
  @automatic_dir
end

#base_dirObject

The base directory. Everything under that directory are expected to be local files and will be installed by the typekit's cmake code



589
590
591
# File 'lib/orogen/gen/typekit.rb', line 589

def base_dir
  @base_dir
end

#imported_typekitsObject (readonly)

Returns the value of attribute imported_typekits



687
688
689
# File 'lib/orogen/gen/typekit.rb', line 687

def imported_typekits
  @imported_typekits
end

#imported_typelistObject

Returns the value of attribute imported_typelist



686
687
688
# File 'lib/orogen/gen/typekit.rb', line 686

def imported_typelist
  @imported_typelist
end

#imported_typesObject (readonly)

Returns the value of attribute imported_types



685
686
687
# File 'lib/orogen/gen/typekit.rb', line 685

def imported_types
  @imported_types
end

#include_dirsObject

The set of include directories that should be considered in #load



629
630
631
# File 'lib/orogen/gen/typekit.rb', line 629

def include_dirs
  @include_dirs
end

#included_filesObject (readonly)

The array of includes that have been loaded



631
632
633
# File 'lib/orogen/gen/typekit.rb', line 631

def included_files
  @included_files
end

#internal_dependenciesObject (readonly)

Packages defined in this typekit on which the typekit should depend. See #internal_dependency.



1568
1569
1570
# File 'lib/orogen/gen/typekit.rb', line 1568

def internal_dependencies
  @internal_dependencies
end

#linked_used_librariesObject (readonly)

Returns the value of attribute linked_used_libraries



690
691
692
# File 'lib/orogen/gen/typekit.rb', line 690

def linked_used_libraries
  @linked_used_libraries
end

#loaded_files_dirsObject (readonly)

Set of directories in which the header files that have been loaded lie. It is used to generate the Cflags: line in the pkg-config file



836
837
838
# File 'lib/orogen/gen/typekit.rb', line 836

def loaded_files_dirs
  @loaded_files_dirs
end

#loadsObject (readonly)

The set of headers loaded by #load, as an array of absolute paths



584
585
586
# File 'lib/orogen/gen/typekit.rb', line 584

def loads
  @loads
end

#nameObject

The typekit name



625
626
627
# File 'lib/orogen/gen/typekit.rb', line 625

def name
  @name
end

#opaque_registryObject (readonly)

Returns the value of attribute opaque_registry



702
703
704
# File 'lib/orogen/gen/typekit.rb', line 702

def opaque_registry
  @opaque_registry
end

#opaquesObject (readonly)

Returns the set of opaque definitions that are known by the typekit



694
695
696
# File 'lib/orogen/gen/typekit.rb', line 694

def opaques
  @opaques
end

#pending_load_optionsObject (readonly)

Returns the value of attribute pending_load_options



703
704
705
# File 'lib/orogen/gen/typekit.rb', line 703

def pending_load_options
  @pending_load_options
end

#pending_loadsObject (readonly)

Returns the value of attribute pending_loads



704
705
706
# File 'lib/orogen/gen/typekit.rb', line 704

def pending_loads
  @pending_loads
end

#pluginsObject (readonly)

The set of code generation plugins



877
878
879
# File 'lib/orogen/gen/typekit.rb', line 877

def plugins
  @plugins
end

#projectObject (readonly)

The Project instance this typekit is part of. It may be nil if the Typekit is generated standalone (as, for instance, in typegen)



581
582
583
# File 'lib/orogen/gen/typekit.rb', line 581

def project
  @project
end

#registryObject (readonly)

Returns the value of attribute registry



684
685
686
# File 'lib/orogen/gen/typekit.rb', line 684

def registry
  @registry
end

#selected_typesObject (readonly)

Returns the value of attribute selected_types



682
683
684
# File 'lib/orogen/gen/typekit.rb', line 682

def selected_types
  @selected_types
end

#template_instanciation_filesObject (readonly)

Returns the value of attribute template_instanciation_files



1915
1916
1917
# File 'lib/orogen/gen/typekit.rb', line 1915

def template_instanciation_files
  @template_instanciation_files
end

#templates_dirObject

The directory in which new versions of the user files should be generated. If nil, they will not be generated at all



623
624
625
# File 'lib/orogen/gen/typekit.rb', line 623

def templates_dir
  @templates_dir
end

#used_librariesObject (readonly)

Returns the value of attribute used_libraries



689
690
691
# File 'lib/orogen/gen/typekit.rb', line 689

def used_libraries
  @used_libraries
end

#user_dirObject

The directory in which files that the user should modify should be saved



620
621
622
# File 'lib/orogen/gen/typekit.rb', line 620

def user_dir
  @user_dir
end

#versionObject

The typekit version string



627
628
629
# File 'lib/orogen/gen/typekit.rb', line 627

def version
  @version
end

Class Method Details

.register_plugin(klass) ⇒ Object

Register a new plugin class. The plugin name is taken from klass.name



575
576
577
# File 'lib/orogen/gen/typekit.rb', line 575

def self.register_plugin(klass)
    plugins[klass.name] = klass
end

.transport_plugin_name(transport_name, typekit_name) ⇒ Object

Raises:

  • (ArgumentError)


879
880
881
882
883
884
885
886
887
888
889
890
# File 'lib/orogen/gen/typekit.rb', line 879

def self.transport_plugin_name(transport_name, typekit_name)
    plugins.each_value do |plg|
        if plg.name == transport_name
            if plg.respond_to?(:plugin_name)
                return plg.plugin_name(typekit_name)
            else
                return "orogen_typekits::#{typekit_name}#{transport_name.capitalize}TransportPlugin"
            end
        end
    end
    raise ArgumentError, "invalid transport name #{transport_name}"
end

Instance Method Details

#build_type(type_name) ⇒ Object



751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
# File 'lib/orogen/gen/typekit.rb', line 751

def build_type(type_name)
    registry.build(type_name)
rescue Typelib::NotFound
    if type_name =~ /(.*)\[(\d+)\]$/
        base_type, array_size = $1, $2
        find_type(base_type, true)
        return registry.build(type_name)
    end

    container_name, template_arguments = Typelib::CXX.parse_template(type_name)
    if template_arguments.size == 1
        element_type = find_type(template_arguments[0], true)
        if project
            project.registry.create_container(container_name,
                            project.find_type(element_type.name, true))
        end
        return registry.create_container(container_name, element_type)

    elsif project && type = project.registry.build(type_name)
        while type.respond_to?(:deference)
            type = type.deference
        end

        type_def = project.registry.minimal(type.name)
        registry.merge(type_def)
        return registry.build(type_name)
    end
end

#cmake_relative_path(file, *subdir) ⇒ Object



2210
2211
2212
# File 'lib/orogen/gen/typekit.rb', line 2210

def cmake_relative_path(file, *subdir)
    "#{Pathname.new(file).relative_path_from(Pathname.new(File.join(automatic_dir, *subdir)))}"
end

#code_fromIntermediate(intermediate_type, needs_copy, indent) ⇒ Object

Helper method that returns the code needed to update an opaque type based on the data from an intermediate variable



1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
# File 'lib/orogen/gen/typekit.rb', line 1904

def code_fromIntermediate(intermediate_type, needs_copy, indent)
    if intermediate_type < Typelib::ArrayType
        "#{indent}orogen_typekits::fromIntermediate(value, length, &intermediate[0]);\n"
    elsif needs_copy
        "#{indent}orogen_typekits::fromIntermediate(value, intermediate);\n"
    else
        "#{indent}if (orogen_typekits::fromIntermediate(value, intermediate.get()))\n" +
        "#{indent}    intermediate.release();\n"
    end
end

#code_toIntermediate(intermediate_type, needs_copy, indent) ⇒ Object

Helper method that returns the code needed to get an intermediate variable of the right type, containing the data in value.



1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
# File 'lib/orogen/gen/typekit.rb', line 1889

def code_toIntermediate(intermediate_type, needs_copy, indent)
    if intermediate_type < Typelib::ArrayType
        "#{indent}std::vector< #{intermediate_type.deference.cxx_name} > intermediate;\n" +
        "#{indent}intermediate.resize(length);\n" +
        "#{indent}orogen_typekits::toIntermediate(&intermediate[0], value, length);\n"
    elsif needs_copy
        "#{indent}#{intermediate_type.cxx_name} intermediate;\n" +
        "#{indent}orogen_typekits::toIntermediate(intermediate, value);\n"
    else
        "#{intermediate_type.cxx_name} const& intermediate = orogen_typekits::toIntermediate(value);"
    end
end

#compute_orogen_include_on_type(type, file_to_include) ⇒ Object



1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
# File 'lib/orogen/gen/typekit.rb', line 1359

def compute_orogen_include_on_type(type, file_to_include)
    if includes = orogen_include_of_type(type, file_to_include)
        type..set('orogen_include', *includes)
        includes
    elsif has_pending_loads?
        perform_pending_loads
        compute_orogen_include_on_type(type, file_to_include)
    else raise ArgumentError, "cannot compute include information for #{type.name}. If it is an opaque, you must either load the header which defines it with import_types_from, or specify the relevant information with the orogen_include option"
    end
end

#copy_metadata_to_intermediate_type(source, dest) ⇒ Object



1834
1835
1836
1837
1838
1839
1840
# File 'lib/orogen/gen/typekit.rb', line 1834

def (source, dest)
    source.each do |key, values|
        if key !~ /codegen/ && !COPY_METADATA_EXCLUDED_KEYS.include?(key)
            dest.set(key, *values)
        end
    end
end

#cxx_gen_includes(*includes) ⇒ String

Given a set of includes, returns the code that includes them in a C++ file

Parameters:

  • includes (String)

Returns:

  • (String)


2252
2253
2254
2255
2256
# File 'lib/orogen/gen/typekit.rb', line 2252

def cxx_gen_includes(*includes)
    includes.to_set.map do |inc|
        "#include <#{inc}>"
    end.sort.join("\n") + "\n"
end

#dependenciesObject

Returns the set of pkg-config packages this typekit depends on



1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
# File 'lib/orogen/gen/typekit.rb', line 1668

def dependencies
    result = []

    # We must link to libraries in case the types we are getting
    # from them has constructors/destructors that the library
    # provides.
    used_libraries.each do |pkg|
        needs_link = linked_used_libraries.include?(pkg)
        result << BuildDependency.new(pkg.name.upcase, pkg.name).
            in_context('core', 'include')
        if needs_link
            result.last.in_context('core', 'link')
        end
    end

    # We must link to typekits that define our types, as we are
    # going to reuse the convertion functions that they define
    used_typekits.each do |tk|
        next if tk.virtual?
        result << BuildDependency.new(
            tk.name.upcase + "_TYPEKIT", tk.pkg_name).
            in_context('core', 'include').
            in_context('core', 'link')
    end

    # We must include the typekits that define types that are used
    # in the other typekits types
    each_plugin do |plg|
        if !plg.separate_cmake?
            if deps = plg.dependencies
                result.concat(deps)
            end
        end
    end

    result.to_a.sort_by { |dep| dep.var_name }
end

#do_import(registry, path, kind, options) ⇒ Object



1842
1843
# File 'lib/orogen/gen/typekit.rb', line 1842

def do_import(registry, path, kind, options)
end

#each_plugin(&block) ⇒ Object

Enumerate all enabled plugins. See #enable_plugin



935
# File 'lib/orogen/gen/typekit.rb', line 935

def each_plugin(&block); plugins.each(&block) end

#enable_plugin(name) ⇒ Object

Add a generation plugin to the generation stage

Returns:

  • (Object)

    the newly created plugin



895
896
897
898
899
900
901
902
903
904
905
906
907
# File 'lib/orogen/gen/typekit.rb', line 895

def enable_plugin(name)
    if plugins.any? { |plg| plg.name == name }
        # It is already there
        return
    end

    if !(plugin = Typekit.plugins[name])
        raise ArgumentError, "there is not typekit plugin called #{name}"
    end
    p = plugin.new(self)
    plugins << p
    p
end

#existing_orogen_include_for_type(type) ⇒ Array?

Returns an existing orogen_include metadata entry for the given type, or nil if none exists so far

Parameters:

  • type (Model<Type>)

    a type model

Returns:

  • (Array, nil)

    either a non-empty set of entries for orogen_include or nil if none could be found



1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
# File 'lib/orogen/gen/typekit.rb', line 1290

def existing_orogen_include_for_type(type)
    if imported_types.include?(type.name)
         = imported_types.get(type.name).
        if .include?('orogen_include')
            return .get('orogen_include')
        end
    end
    if self.registry.include?(type.name)
         = self.registry.get(type.name).
        if .include?('orogen_include')
            return .get('orogen_include')
        end
    end
     = type.
    if .include?('orogen_include')
        return .get('orogen_include')
    end
    nil
end

#export_types(*selection) ⇒ Object

Select a set of types to be exported through the RTT type system, instead of exporting everything. This is meant to reduce the typekit's code size and compilation times tremendously.

See also #type_export_policy



676
677
678
679
680
# File 'lib/orogen/gen/typekit.rb', line 676

def export_types(*selection)
    @selected_types |= selection.map { |name| find_type(name) }.to_set
rescue Typelib::NotFound => e
    raise ConfigError, e.message, e.backtrace
end

#exported_type?(typename) ⇒ Boolean

Returns true if typename can be used on a task context interface

Returns:

  • (Boolean)


745
746
747
748
749
# File 'lib/orogen/gen/typekit.rb', line 745

def exported_type?(typename)
    imported_typekits_for(typename).any? do |tk|
        tk.interface_type?(typename)
    end
end

#filter_unsupported_types(registry) ⇒ Object



1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
# File 'lib/orogen/gen/typekit.rb', line 1228

def filter_unsupported_types(registry)
    to_delete = Set.new
    registry.each do |type|
        # Multi-dimensional arrays are forbidden in CORBA IDL
        # TODO: work around this in Typelib and oroGen by emitting a
        # TODO: single-dimension array of the right size
        if type < Typelib::ArrayType && type.deference < Typelib::ArrayType
            RTT_CPP.warn "ignoring #{type.name} as multi-dimensional arrays cannot be represented in CORBA IDL"
            to_delete << type

        elsif type < Typelib::PointerType
            RTT_CPP.warn "ignoring #{type.name} as pointers are not allowed"
            to_delete << type

        elsif type.name == "/std/vector</bool>"
            RTT_CPP.warn "std::vector<bool> is unsupported in oroGen due to its special nature. Use std::vector<uint8_t> instead."
            to_delete << type

        elsif type < Typelib::CompoundType
            type.each_field do |field_name, _|
                if field_name !~ /^[a-zA-Z]/
                    RTT_CPP.warn "ignoring #{type.name} as its field #{field_name} does not start with an alphabetic character, which is forbidden in CORBA IDL"
                    to_delete << type
                    break
                end
            end
        end
    end

    to_delete.each do |type|
        deleted_types = registry.remove(type)
        deleted_types.each do |dep_type|
            next if to_delete.include?(dep_type)
            RTT_CPP.warn "ignoring #{dep_type.name} as it depends on #{type.name} which is ignored"
        end
    end
end

#find_plugin(name) ⇒ Object

Looks for an enabled plugin called name

Parameters:

  • name (String)

    the name of the plugin to look for

Returns:

  • (Object)

    the plugin object, or nil if it cannot be found

See Also:



915
916
917
# File 'lib/orogen/gen/typekit.rb', line 915

def find_plugin(name)
    return plugins.find { |p| p.name == name }
end

#find_type(type, is_normalized = false) ⇒ Object

Returns the Typelib::Type subclass that represents the type whose name is given. If the type is a derived type (pointer, array or container), then it will be built on the fly.



783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
# File 'lib/orogen/gen/typekit.rb', line 783

def find_type(type, is_normalized = false)
    if type.respond_to?(:to_str)
        type_name = type.gsub('::', '/')
        if !is_normalized
            type_name = Typelib::Type.normalize_typename(type_name)
            is_normalized = true
        end
        begin
            registry.get(type_name)
        rescue Typelib::NotFound
            new_type = build_type(type_name)
            compute_orogen_include_on_type(new_type, Hash.new)
            return new_type
        end
    elsif type.kind_of?(Class) && type <= Typelib::Type
        if !registry.include?(type.name)
            type_def = type.registry.minimal(type.name)
            registry.merge(type_def)
            if project
                project.registry.merge(type_def)
            end
            new_type = registry.get(type.name)
            compute_orogen_include_on_type(new_type, Hash.new)
            return new_type
        else
            return registry.get(type.name)
        end
    else
        raise ArgumentError, "expected a type object or a type name, but got #{type} (#{type.class})"
    end

rescue Typelib::NotFound => e
    if !pending_loads.empty?
        perform_pending_loads
        retry
    end
    raise e.class, e.message, e.backtrace
end

#generateObject



1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
# File 'lib/orogen/gen/typekit.rb', line 1975

def generate
    typekit = self

    FileUtils.mkdir_p automatic_dir

    # Populate a fake installation directory so that the include
    # files can be referred to as <project_name>/header.h
    fake_install_dir = File.join(automatic_dir, name)

    # Upgrade from directory to symlink
    if !File.symlink?(fake_install_dir)
        FileUtils.rm_rf fake_install_dir
    end
    Generation.create_or_update_symlink(automatic_dir, fake_install_dir)

    if standalone?
        fake_typekit_dir = File.join(automatic_dir, "typekit")
        Generation.create_or_update_symlink(automatic_dir, fake_typekit_dir)
    end

    # Generate opaque-related stuff first, so that we see them in
    # the rest of the typelib-registry-manipulation code
    handle_opaques_generation(registry)
    perform_pending_loads

    # Do some registry mumbo-jumbo to remove unneeded types to the
    # dumped registry
    @registry = normalize_registry
    minimal_registry = @registry.dup
    generated_types = self_types.to_set

    self_opaques = self.self_opaques.sort_by { |opdef| opdef.type.name }

    issue_warnings(generated_types, minimal_registry)
    generate_typedefs(generated_types, minimal_registry)

    # The first array is the set of types for which convertion
    # functions are generated. The second is the set of types that
    # are actually registered into the RTT type system
    #
    # The two arrays are sorted so that we don't have to recompile
    # unnecessarily (the original sets are hashes, and therefore don't
    # have a stable order).
    converted_types = generated_types.
        find_all { |type| !(type <= Typelib::NumericType) }.
        sort_by { |type| type.name }

    # We need a special case for arrays. The issue is the following:
    # for arrays, the convertion functions take pointers as input.
    # So we generate only one convertion function for all arrays of
    # the same type, regardless of the size.
    converted_types, array_types = converted_types.
        partition { |type| !(type < Typelib::ArrayType) }

    array_types = array_types.
        delete_if do |type|
            imported_array_of?(type.deference)
        end.
        inject(Hash.new) { |h, type| h[type.deference.name] = type; h }.
        values.
        sort_by { |type| type.name }

    # If the selected type export policy is used, check if it makes
    # sense. If it does not, switch back to 'all'
    if type_export_policy == :used && (!project || project.self_tasks.empty?)
        type_export_policy :all
    end

    # Get the types that should be registered in the RTT type
    # system. Note that we must map these types to +registry+ as
    # +registry+ has been recomputed (i.e. it changes between the
    # specification time and the generation time)
    #
    # Only +generated_types+ does not need to be mapped as it has
    # just been computed
    registered_types =
        if type_export_policy == :all
            generated_types.find_all do |type|
                !m_type?(type) && !(type <= Typelib::NumericType)
            end.to_set

        elsif type_export_policy == :used
            used_types = project.self_tasks.inject(Set.new) do |result, task|
                result | map_typeset_to_registry(registry, task.interface_types)
            end
            (used_types & generated_types)
        elsif type_export_policy == :selected
            map_typeset_to_registry(registry, selected_types)
        end

    if !selected_types.empty?
        registered_types |= map_typeset_to_registry(registry, selected_types)
    end

    # Save all the types that this specific typekit handles,
    # including aliases
    registered_typenames = registered_types.map(&:name).to_set
    all_names = Hash.new
    minimal_registry.each(:with_aliases => true) do |name, type|
        all_names[type.name] ||= []
        all_names[type.name] << name
    end
    typelist_txt = []
    generated_types.each do |type|
        is_exported = registered_typenames.include?(type.name) ? '1' : '0'
         = minimal_registry.get(type.name).
        .add 'orogen_defining_typekits', self.name
        if is_exported
            .add 'orogen_exporting_typekits', self.name
        end
        all_names[type.name].each do |type_name|
            typelist_txt << "#{type_name} #{is_exported}"
        end
    end
    save_automatic "#{name}.typelist",
        typelist_txt.sort.join("\n")

    # Generate the XML representation of the generated type library,
    # and add opaque information to it
    plain_registry = minimal_registry.to_xml
    doc = REXML::Document.new(plain_registry)
    doc.each_element('//opaque') do |opaque_entry|
        spec = opaque_specification(opaque_entry.attributes['name'])
        opaque_entry.add_attributes(
            'marshal_as' => spec.intermediate,
            'includes' => spec.includes.join(':'),
            'needs_copy' => (spec.needs_copy? ? '1' : '0'))
    end

    modified_tlb = String.new
    doc.write(modified_tlb)
    save_automatic "#{name}.tlb", modified_tlb

    registered_types = registered_types.
        sort_by { |t| t.name }
    registered_types.delete_if do |t|
        t.contains_opaques? && t < Typelib::ArrayType
    end
    interface_types = registered_types.
        find_all { |t| !(t < Typelib::ArrayType) }

    type_sets = TypeSets.new
    type_sets.types            = generated_types.sort_by { |t| t.name }
    type_sets.converted_types  = converted_types
    type_sets.array_types      = array_types
    type_sets.registered_types = registered_types
    type_sets.interface_types  = interface_types
    type_sets.minimal_registry = minimal_registry
    type_sets.opaque_types     = self_opaques
    type_sets.aliases          = Hash.new

    public_header_files, plugin_header_files, implementation_files = [], [], []

    # For backward compatibility
    if File.symlink?(old_symlink = File.join(automatic_dir, 'Opaques.hpp'))
        FileUtils.rm_f old_symlink
    end

    type_header = Generation.render_template('typekit/Types.hpp', binding)
    public_header_files << save_automatic_public_header("Types.hpp", type_header)
    type_header = Generation.render_template('typekit/TypesDeprecated.hpp', binding)
    save_automatic("Types.hpp", type_header)
    type_header = Generation.render_template('typekit/OpaquesDeprecated.hpp', binding)
    save_automatic("Opaques.hpp", type_header)
    boost_serialization = Generation.render_template "typekit/BoostSerialization.hpp", binding
    public_header_files << save_automatic_public_header("BoostSerialization.hpp", boost_serialization)
    tk_hpp = Generation.render_template "typekit/Plugin.hpp", binding
    public_header_files << save_automatic_public_header("Plugin.hpp", tk_hpp)
    tk_cpp = Generation.render_template "typekit/Plugin.cpp", binding
    implementation_files << save_automatic("Plugin.cpp", tk_cpp)

    # Generate the opaque convertion files
    if has_opaques?
        intermediates_hpp = Generation.render_template 'typekit/OpaqueConvertions.hpp', binding
        public_header_files <<
            save_automatic_public_header("OpaqueConvertions.hpp", intermediates_hpp)

        intermediates_cpp = Generation.render_template 'typekit/OpaqueConvertions.cpp', binding
        implementation_files <<
            save_automatic("OpaqueConvertions.cpp", intermediates_cpp)

        fwd_hpp = Generation.render_template 'typekit/OpaqueFwd.hpp', binding
        public_header_files <<
            save_automatic_public_header("OpaqueFwd.hpp", fwd_hpp)

        types_hpp = Generation.render_template 'typekit/OpaqueTypes.hpp', binding
        public_header_files <<
            save_automatic_public_header("OpaqueTypes.hpp", types_hpp)

        if has_opaques_with_templates?
            user_hh = Generation.render_template 'typekit/Opaques.hpp', binding
            user_cc = Generation.render_template 'typekit/Opaques.cpp', binding
            save_user("Opaques.hpp", user_hh)
            implementation_files <<
                save_user("Opaques.cpp", user_cc)

            Generation.create_or_update_symlink(
                File.join(user_dir, "Opaques.hpp"),
                File.join(automatic_dir, INCLUDE_DIR_NAME, self.name, 'typekit', "Opaques.hpp"))
        end
    end

    each_plugin do |plg|
        plg_typesets = type_sets.dup
        headers, impl = plg.generate(plg_typesets)
        plugin_header_files.concat(headers)
        implementation_files.concat(impl)
    end

    if standalone?
        FileUtils.mkdir_p File.join(automatic_dir, 'config')
        Dir.glob File.join(Generation.template_path('config'), '*') do |path|
            basename    = File.basename(path)
            if !Project::CMAKE_GENERATED_CONFIG.include?(basename)
                save_automatic 'config', basename, File.read(path)
            end
        end
    end

    pkg_config = Generation.render_template 'typekit/typekit.pc', binding
    save_automatic("#{name}-typekit.pc.in", pkg_config)
    cmake = Generation.render_template 'typekit', 'CMakeLists.txt', binding
    save_automatic("CMakeLists.txt", cmake)
    manifest = Generation.render_template 'typekit', 'manifest.xml', binding
    save_automatic("manifest.xml", manifest)
    package = Generation.render_template 'typekit', 'package.xml', binding
    save_automatic("package.xml", package)
    makefile = Generation.render_template 'typekit', 'Makefile', binding
    save_automatic("Makefile", makefile)

    # Finished, create the timestamp file
    Generation.cleanup_dir(automatic_dir)
    Generation.touch File.join(automatic_dir, 'stamp')
end

#generate_typedefs(generated_types, registry) ⇒ Object

This generates typedefs for container types. These typedefs are needed because IDL and the CORBA C++ mapping do not allow to reference sequence types directly (you have to typedef them first, or use them in a compound)



1849
1850
# File 'lib/orogen/gen/typekit.rb', line 1849

def generate_typedefs(generated_types, registry)
end

#handle_local_load(file) ⇒ Object

Handle a load that points to a file in this typekit's source tree



1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
# File 'lib/orogen/gen/typekit.rb', line 1134

def handle_local_load(file)
    rel = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
    return file if rel.each_filename.first == ".."

    local_type_dir = Pathname.new(includes_dir)
    rel_to_type_dir = Pathname.new(file).relative_path_from(local_type_dir)
    if rel_to_type_dir.each_filename.first == ".."
        # If the type is within a subdirectory called as the
        # typekit, remove the duplicate
        elements = rel.each_filename.to_a
        if elements.first != self.name
            elements.unshift self.name
        end
        target = File.join(includes_dir, *elements)
        Generation.create_or_update_symlink(file, target)
        target
    else file
    end
end

#handle_opaques_generation(generated_types) ⇒ Object



1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
# File 'lib/orogen/gen/typekit.rb', line 1774

def handle_opaques_generation(generated_types)
    # Make sure all opaque intermediate types are existing or can be
    # instanciated
    self_opaques.each do |opaque_def|
        begin
            t = find_type(opaque_def.intermediate)
            (opaque_def.type., t.)
            if t.contains_opaques?
                raise ConfigError, "the type #{opaque_def.intermediate} is used as an intermediate type for #{opaque_def.type.name}, but it is an opaque or contains opaques"
            end
        rescue Typelib::NotFound
            raise ConfigError, "type #{opaque_def.intermediate}, used as intermediate for opaque #{opaque_def.type.name}, does not exist", opaque_def.caller
        end
    end

    # Generate some type definitions for the pocosim marshalling. In
    # practice, we generate C code that we merge back into the
    # repository
    needed_definitions = self_types.
        find_all { |t| t.contains_opaques? && !t.opaque? }

    # Sort the types by the order in which we should generate their
    # intermediates. This is needed when one m-type requires another
    # m-type that we generate in this typekit as well
    to_generate = []
    while !needed_definitions.empty?
        needed_definitions.delete_if do |type|
            next(true) if m_type_exists?(type)
            next if type.dependencies.any? { |t| needed_definitions.include?(t) }
            to_generate << type
        end
    end

    options = { :include => include_dirs.dup }

    to_generate.each do |type|
        needed_type_definitions = type.direct_dependencies.map do |needed_type|
            find_type(intermediate_type_for(needed_type), true)
        end
        typekit = self
        marshalling_code = Generation.
            render_template 'typekit', 'marshalling_types.hpp', binding

        path = Generation.save_automatic 'typekit', 'types', self.name, "m_types", "#{type.method_name(true)}.hpp", marshalling_code
        self.load(path, true, options)

        m_type = intermediate_type_for(type)
        (type., m_type.)
        if type.respond_to?(:field_metadata) && m_type.respond_to?(:field_metadata)
            m_type..each do |field_name, |
                (type.[field_name], m_type.[field_name])
            end
        end
        m_type..set 'orogen:m_type', '1'
        m_type..add 'orogen:intermediate_for', type.name
    end
    true
end

#has_opaques?Boolean

True if there are some opaques in this typekit. The result of this method is only valid during generation. Don't use it in general.

Returns:

  • (Boolean)


1124
1125
1126
# File 'lib/orogen/gen/typekit.rb', line 1124

def has_opaques?
    self_types.any? { |t| t.contains_opaques? }
end

#has_opaques_with_templates?Boolean

True if some opaques require to generate templates

Returns:

  • (Boolean)


1129
1130
1131
# File 'lib/orogen/gen/typekit.rb', line 1129

def has_opaques_with_templates?
    self_opaques.any? { |op| op.generate_templates? }
end

#has_pending_loads?Boolean

Returns:

  • (Boolean)


1489
1490
1491
# File 'lib/orogen/gen/typekit.rb', line 1489

def has_pending_loads?
    !pending_loads.empty?
end

#import(other_typekit) ⇒ Object

Raises:

  • (NotImplementedError)


1584
1585
1586
# File 'lib/orogen/gen/typekit.rb', line 1584

def import(other_typekit)
    raise NotImplementedError
end

#imported_array_of?(type) ⇒ Boolean

Returns:

  • (Boolean)


1626
1627
1628
1629
1630
1631
1632
# File 'lib/orogen/gen/typekit.rb', line 1626

def imported_array_of?(type)
    typename = if type.respond_to?(:name) then type.name
               else type.to_str
               end

    imported_typelist.any? { |str| str =~ /#{Regexp.quote(typename)}(\[\d+\])+/ }
end

#imported_type?(typename) ⇒ Boolean

Returns true if typename has been defined by a typekit imported by using_typekit

Returns:

  • (Boolean)


740
741
742
# File 'lib/orogen/gen/typekit.rb', line 740

def imported_type?(typename)
    !selected_types.find { |t| t.name == typename } && !imported_typekits_for(typename).empty?
end

#imported_typekits_for(typename) ⇒ Object

Returns the typekit object that defines this type



731
732
733
734
735
736
# File 'lib/orogen/gen/typekit.rb', line 731

def imported_typekits_for(typename)
    if typename.respond_to?(:name)
        typename = typename.name
    end
    return imported_typekits.find_all { |tk| tk.includes?(typename) }
end

#include_for_type(type) ⇒ Array<String>

Returns the set of includes that should be added to get access to the given type

Parameters:

Returns:

  • (Array<String>)

See Also:



2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
# File 'lib/orogen/gen/typekit.rb', line 2234

def include_for_type(type)
    if includes = existing_orogen_include_for_type(type)
        return includes.map { |s| s.split(':').last }
    end

    if type.opaque?
        raise ConfigError, "no includes known for #{type.name}, This is an opaque, and you must either call import_types_from on a header that defines it, or provide the :include option to the opaque definition"
    else
        raise InternalError, "no includes known for #{type.name} defined in #{type..get("source_file_line")}"
    end
end

#includes_dirObject

Full path to the directory in which includes are either generated or symlinked



611
612
613
# File 'lib/orogen/gen/typekit.rb', line 611

def includes_dir
    File.join(automatic_dir, INCLUDE_DIR_NAME)
end

#intermediate_cxxname_for(type_def) ⇒ Object



1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
# File 'lib/orogen/gen/typekit.rb', line 1708

def intermediate_cxxname_for(type_def)
    type = find_type(type_def)
    if type < Typelib::ArrayType
        prefix, suffix = intermediate_cxxname_for(type.deference)
        [prefix, "#{suffix}[#{type.length}]"]
    else
        name = intermediate_type_name_for(type)
        [name.gsub('/', '::').gsub('<::', '< ::').gsub('>>','> >')]
    end
end

#internal_dependency(name, version = nil) ⇒ Object

The second one allows to specify a dependency of the typekit on a library defined in the same CMake package. The typekit's .pc file will therefore depend on the specified pkg-config package (among other things). Do that when you define a library of types and want people to be able to use it even though they don't have orogen.

In other words, it allows to build packages were:

* a normal C/C++ library is defined/built/install
* an orogen typekit is defined, which wraps the types defined by this library


1580
1581
1582
# File 'lib/orogen/gen/typekit.rb', line 1580

def internal_dependency(name, version = nil)
    @internal_dependencies << [name, version]
end

#issue_warnings(generated_types, registry) ⇒ Object



1852
1853
1854
1855
1856
1857
1858
# File 'lib/orogen/gen/typekit.rb', line 1852

def issue_warnings(generated_types, registry)
    generated_types.each do |type|
        if type.contains_int64?
            OroGen::Gen::RTT_CPP.info "you will not be able to marshal #{type.name} as XML, it contains 64bit integers"
        end
    end
end

#linux?Boolean

True if the orocos target is gnulinux

Returns:

  • (Boolean)


829
# File 'lib/orogen/gen/typekit.rb', line 829

def linux?; orocos_target == 'gnulinux' end

#load(file, add = true, user_options = Hash.new) ⇒ Object

call-seq:

load(file)

Load the types defined in the specified file.

file may contain pure-C code with the following C++ additions:

  • namespaces

  • std::vector< type >. You have to specify std::vector (and not simply vector, as the using namespace directive is not supported by orogen.

Moreover, the orogen tool defines the __orogen preprocessor symbol when it loads the file. It is therefore possible to define constructors, destructors, operators and (more generically) methods by enclosing them in a block like

#ifndef __orogen
... C++ code not supported by orogen ...
#endif

The use of virtual methods and inheritance is completely forbidden. The types need to remain “value types” without inheritance. For those who want to know, this is needed so that orogen is able to compute the memory layout of the types (i.e. the exact offsets for all the fields in the structures).



1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
# File 'lib/orogen/gen/typekit.rb', line 1180

def load(file, add = true, user_options = Hash.new)
    if !user_options.respond_to?(:to_hash) 
        raise ArgumentError, "expected an option has as third argument, got #{user_options.inspect}"
    end

    if match = /(\w+)\/Types\.hpp$/.match(file)
        project_name = match[1]
        if project.has_typekit?(project_name) || project.name == project_name
            raise ArgumentError, "cannot use a header called #{file} as it clashes with the Types.hpp header generated by orogen for the #{project_name} project"
        end
    end

    include_dirs = self.include_dirs

    # Get the full path for +file+
    file =
        if File.file?(file) # Local file
            File.expand_path(file)
        else # File from used libraries/task libraries
            dir = include_dirs.find { |dir| File.file?(File.join(dir, file)) }
            if !dir
                raise LoadError, "cannot find #{file} in #{include_dirs.to_a.join(":")}"
            end
            loaded_files_dirs << dir
            File.join(dir, file)
        end

    # If it is a local header, symlink it to the "right" place in
    # typekit/types and load that
    file = handle_local_load(file)

    # And resolve it to an include statement
    include_path = include_dirs.map { |d| Pathname.new(d) }
    inc = resolve_full_include_path_to_relative(file, include_path)
    included_files << inc
    user_options[:rawflags] = self.used_libraries.
        map { |lib| lib.raw_cflags_only_other }.
        flatten.uniq

    this_options = [add, user_options]
    if pending_load_options != this_options
        perform_pending_loads
    end

    @pending_load_options = this_options
    pending_loads << file
end

#m_type_exists?(type) ⇒ Boolean

Returns:

  • (Boolean)


1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
# File 'lib/orogen/gen/typekit.rb', line 1724

def m_type_exists?(type)
    target_typename = intermediate_type_name_for(type)
    current_def = begin
                      find_type(target_typename)
                  rescue Typelib::NotFound
                  end

    return false if !current_def

    expected_type = type
    while current_def.respond_to?(:deference)
        current_def = current_def.deference
        if !expected_type.respond_to?(:deference)
            raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?"
        end
        expected_type = expected_type.deference
    end

    if current_def < Typelib::CompoundType
        if expected_type.opaque?
            # nothing to do
        elsif !(expected_type < Typelib::CompoundType)
            raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?"
        elsif current_def.fields.size != expected_type.fields.size
            raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?"
        else
            type_fields    = expected_type.fields.dup
            current_fields = current_def.fields.dup

            while !type_fields.empty?
                expected = type_fields.first
                current  = current_fields.first

                if expected[0] != current[0] ||
                    !expected[1].opaque? && expected[1] != current[1]
                    raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?"
                elsif expected[1].opaque?
                    if intermediate_type_for(expected[1]) != current[1]
                        raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?"
                    end
                end

                type_fields.pop
                current_fields.pop
            end
        end
    end
    true
end

#m_types_codeObject



1720
1721
1722
# File 'lib/orogen/gen/typekit.rb', line 1720

def m_types_code
    @m_types_code
end

#make_load_options(pending_loads, user_options) ⇒ Object



1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
# File 'lib/orogen/gen/typekit.rb', line 1452

def make_load_options(pending_loads, user_options)
    options = { :opaques_ignore => true, :merge => false, :required_files => pending_loads.to_a }
    # GCCXML can't parse vectorized code, and the Typelib internal
    # parser can't parse eigen at all. It is therefore safe to do it
    # here
    options[:define] = ["OROCOS_TARGET=#{RTT_CPP.orocos_target}", '__orogen2']

    options[:include] = self.include_dirs.dup
    options = options.merge(user_options) do |key, a, b|
        if a.respond_to?(:to_ary)
            if b.respond_to?(:to_ary)
                b.concat(a)
            else
                [b].concat(a)
            end
        else
            b
        end
    end

    opaque_names = opaques.map { |opdef| opdef.type.name }
    options[:opaques] = opaque_names

    # In principle, the import stage should not need any of the
    # include and defines since it is given the preprocessed file.
    # However, the clang importer breaks this and re-preprocesses
    # the file.
    #
    # This works around it by passing the flags to both stages.
    preprocess_options = Hash.new
    preprocess_options[:rawflags]      = options.fetch(:rawflags, Array.new)
    preprocess_options[:include]       = options.fetch(:include, Array.new)
    preprocess_options[:include_paths] = options.fetch(:include, Array.new)
    preprocess_options[:define]        = options.fetch(:define, Array.new)
    return preprocess_options, options
end

#map_typeset_to_registry(registry, types) ⇒ Object

Makes sure that a set of type objects comes from the same registry

In order to use Set, we must make sure that all type objects come from the same registry. This method takes a Set of types and converts all of them to the type coming from registry



1971
1972
1973
# File 'lib/orogen/gen/typekit.rb', line 1971

def map_typeset_to_registry(registry, types)
    types.map { |t| find_type(t) }.to_set
end

#normalize_registryObject



1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
# File 'lib/orogen/gen/typekit.rb', line 1593

def normalize_registry
    base = self.registry.dup

    # Properly define the headers we want to use for cstdint headers
    [1, 2, 4, 8].each do |int_size|
        if base.include?("/int#{int_size * 8}_t")
            base.get("/int#{int_size * 8}_t").
                .set("orogen_include", "boost/cstdint.hpp")
        end
        if base.include?("/uint#{int_size * 8}_t")
            base.get("/uint#{int_size * 8}_t").
                .set("orogen_include", "boost/cstdint.hpp")
        end
    end

    result = Typelib::Registry.new
    self_types.each do |type|
        result.merge(base.minimal(type.name))
    end

    # Also register the intermediate types for our opaques. The
    # resulting registry must be self-contained, and the
    # intermediate stuff is specific to oroGen (i.e. typelib does
    # not handle them for us)
    opaques.each do |op_def|
        if result.include?(op_def.type.name)
            result.merge(base.minimal(find_type(op_def.intermediate).name))
        end
    end

    result
end

#opaque_type(base_type, intermediate_type, options = Hash.new, &convert_code_generator) ⇒ Object

Declare that the user will provide a method which converts base_type into the given intermediate type. Orogen will then use that intermediate representation to marshal the data.

Orogen has a specific support for data held by smart pointers. See #smart_ptr.

The following options are available:

:includes is an optional set of headers needed to define base_type. For instance:

opaque_type "Eigen::Vector3f", "imu::Vector3f", :includes => ["/usr/include/eigen2", "/opt/boost/include"]

If there is only one include, the array can be omitted

opaque_type "Eigen::Vector3f", "imu::Vector3f", :includes => "/usr/include/eigen2"

:needs_copy is a flag telling how the opaque type should be converted. If true (the default), orogen will generate convertion methods whose signature are:

void orogen_typekits::toIntermediate(intermediate_type& intermediate, base_type const& sample)
void orogen_typekits::fromIntermediate(base_type& sample, intermediate_type const& intermediate)

In the first examples above, this would be (assuming an “imu” orogen project)

void orogen_typekits::toIntermediate(imu::Vector3f& intermediate, Eigen::Vector3f const& sample)
void orogen_typekits::fromIntermediate(Eigen::Vector3f& sample, imu::Vector3f& intermediate)

Note that in fromIntermediate, the intermediate argument in non-const. It is actually allows to modify it, as for instance to get better performance (example: use vector<>.swap() instead of doing a big copy).

If :needs_copy is false, then we assume that a copy is not needed. In that case, the toIntermediate convertion method will return the intermediate type directly. The signature will therefore be changed to

intermediate_type& orogen_typekits::toIntermediate(base_type const& sample)
bool orogen_typekits::fromIntermediate(base_type& sample, intermediate_type* intermediate)

Note that in fromIntermediate the intermediate argument is now given as a non-const pointer. The convertion function can choose to take ownership on that value, in which case it has to return true. If the function returns false, then the sample is deleted after the method call.



1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
# File 'lib/orogen/gen/typekit.rb', line 1078

def opaque_type(base_type, intermediate_type, options = Hash.new, &convert_code_generator)
    options = Kernel.validate_options options,
        :include => [],
        :includes => [],
        :orogen_include => [],
        :needs_copy => true

    if options[:includes].respond_to?(:to_str)
        options[:includes] = [options[:includes]]
    end
    if options[:include].respond_to?(:to_str)
        options[:include] = [options[:include]]
    end
    options[:include].concat(options.delete(:includes))

    base_type = base_type.to_str
    base_type = Typelib::Type.normalize_typename(base_type)
    if intermediate_type.kind_of?(Class) && intermediate_type < Typelib::Type
        intermediate_type = intermediate_type.name
    else
        intermediate_type = Typelib::Type.normalize_typename(intermediate_type)
    end

    typedef = "<typelib><opaque name=\"#{base_type.gsub('<', '&lt;').gsub('>', '&gt;')}\" size=\"#{0}\" /></typelib>"
    opaque_def = Typelib::Registry.from_xml(typedef)
    opaque_registry.merge opaque_def
    registry.merge opaque_def
    if project
        project.registry.merge opaque_def
    end

    opaque_type = find_type(base_type, true)
    options[:include].each do |inc|
        opaque_type..add('orogen_include', "#{self.name}:#{inc}")
    end
    orogen_def  = OpaqueDefinition.new(opaque_type,
                                     intermediate_type, options, convert_code_generator) 
    orogen_def.caller = caller
    @opaques << orogen_def
    @opaques = opaques.
        sort_by { |orogen_def| orogen_def.type.name }
    opaque_type
end

#orocos_targetObject

The target operating system for orocos. Uses the OROCOS_TARGET environment variable, if set, and defaults to gnulinux otherwise.



824
825
826
# File 'lib/orogen/gen/typekit.rb', line 824

def orocos_target
    RTT_CPP.orocos_target.dup
end

#orogen_include_of_type(type, file_to_include) ⇒ Object



1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
# File 'lib/orogen/gen/typekit.rb', line 1310

def orogen_include_of_type(type, file_to_include)
    # 'Types with deference' are vectors and arrays. Arrays
    # are ignored. Vectors are hardcoded to :vector
    if type.respond_to?(:deference)
        deference_includes = orogen_include_of_type(type.deference, file_to_include)
        # if the type used in the opaque/ro_ptr/container has no
        # include, the container doesn't need one as well?
        if !deference_includes
            return
        end

        container_includes = []
        if type <= Typelib::ContainerType
            if type.name == '/std/string'
                container_includes = [':string']
            elsif type.container_kind == '/std/vector'
                container_includes = [':vector']
            else
                raise ArgumentError, "unexpected container type #{type.container_kind}"
            end
        end
        return container_includes + deference_includes
    elsif type <= Typelib::NullType
        return []
    elsif type <= Typelib::NumericType
        if type.integer? then return [':boost/cstdint.hpp']
        else return []
        end
    elsif existing = existing_orogen_include_for_type(type)
        return existing
    else
        if !(location = type..get('source_file_line').first)
            return
        end

        file, line = location.split(':')
        if !File.file?(file)
            RTT_CPP.debug("resolve_registry_includes: deleting non-existing 'line' entry in metadata 'source_file_line'=#{location}")
            type..delete('source_file_line')
            return
        end

        if orogen_include = file_to_include[file][Integer(line)]
            return ["#{self.name}:#{orogen_include}"]
        else raise ArgumentError, "no entry for '#{file}:#{line}' in the provided file-to-include mapping of typekit '#{self.name}'"
        end
    end
end

#perform_pending_loadsObject



1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
# File 'lib/orogen/gen/typekit.rb', line 1493

def perform_pending_loads
    return if pending_loads.empty?
    loads = pending_loads.dup
    pending_loads.clear

    add, user_options = *pending_load_options

    include_dirs = self.include_dirs.to_a
    if automatic_dir
        include_dirs << File.join(automatic_dir, "types")
    end

    file_registry = Typelib::Registry.new
    file_registry.merge opaque_registry

    preprocess_options, options = make_load_options(loads, user_options)
    preprocessed, include_mappings = resolve_toplevel_include_mapping(loads, preprocess_options)

    include_path = include_dirs.map { |d| Pathname.new(d) }
    pending_loads_to_relative = loads.inject(Hash.new) do |map, path|
        map[path] = resolve_full_include_path_to_relative(path, include_path)
        map
    end

    include_mappings.each do |file, lines|
        lines.map! { |inc| pending_loads_to_relative[inc] }
    end

    Tempfile.open(["orogen-pending-loads_",".hpp"]) do |io|
        io.write preprocessed
        io.flush

        begin
            file_registry.import(io.path, 'c', options)
            filter_unsupported_types(file_registry)
            resolve_registry_includes(file_registry, include_mappings)
            validate_related_types(file_registry)
            registry.merge(file_registry)
            if project
                project.registry.merge(file_registry)
            end
        rescue Exception => e
            raise ArgumentError, "cannot load one of the header files #{loads.join(", ")}: #{e.message}", e.backtrace
        end
    end

    if add
        self.loads.concat(loads.to_a)
    end
end

#plugin(name) ⇒ Object

Return the plugin object for name

name

Parameters:

  • name (String)

    the name of the plugin to look for

Returns:

  • (Object)

    the plugin object, or nil if it cannot be found

Raises:

  • (ArgumentError)

    raised if no plugin is enabled with name

See Also:



926
927
928
929
930
931
932
# File 'lib/orogen/gen/typekit.rb', line 926

def plugin(name)
    if plg = find_plugin(name)
        return plg
    else
        raise ArgumentError, "there is no plugin called #{name}"
    end
end

#render_typeinfo_snippets(code_snippets, *place) ⇒ Object



1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
# File 'lib/orogen/gen/typekit.rb', line 1939

def render_typeinfo_snippets(code_snippets, *place)
    return [] if code_snippets.empty?

    impl = []
    place = File.join(*place)
    code_snippets.each do |code|
        if code[0].respond_to?(:name_as_word)
            code[0] = code[0].name_as_word
        else
            code[0] = code[0].to_str
        end
    end
    code_snippets = code_snippets.sort_by { |code| code[0] }

    slice_size = RTT_CPP.typekit_slice
    while slice_size > 1 && code_snippets.size / slice_size < RTT_CPP.typekit_slice_minimum
        slice_size -= 1
    end

    code_snippets.each_slice(slice_size) do |code|
        file_name = code.map(&:first).join("_")
        code = code.map(&:last).join("\n")
        impl << save_automatic(place, "#{file_name}.cpp", code)
    end
    impl
end

#resolve_full_include_path_to_relative(file, include_path) ⇒ String

Resolves a full path into the one that should be used when taking into account the include path

Parameters:

  • file (String)

    the full path to the include file

  • include_path (Array<Pathname>)

    the set of include dirs

Returns:

  • (String)


1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
# File 'lib/orogen/gen/typekit.rb', line 1272

def resolve_full_include_path_to_relative(file, include_path)
    file = Pathname.new(file)
    # Find the base include
    include_candidates = include_path.map do |inc|
        rel = file.relative_path_from(inc).cleanpath.to_path
        if rel !~ /^\.\.\//
            rel
        end
    end
    include_candidates.compact.min_by { |inc| inc.size }
end

#resolve_registry_includes(registry, file_to_include) ⇒ Object

Resolves the orogen_include metadata for each type in the given registry

It computes the set of #include <> statements that are required to get access on the C++ side to each type in the registry. It is saved in the orogen_include metadata information of the types.

Parameters:

  • registry (Typelib::Registry)

    whose types should be resolved

  • file_to_include ({String=>Array<String>})

    mapping from a source file and line to the toplevel include that relates to this file



1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
# File 'lib/orogen/gen/typekit.rb', line 1382

def resolve_registry_includes(registry, file_to_include)
    queue = Array.new
    registry.each do |type|
        _, template_args = Typelib::CXX.parse_template(type.name)
        template_args = template_args.map do |type_name|
            if registry.include?(type_name)
                registry.get(type_name)
            end
        end.compact
        queue << [type, template_args]
    end
    queue = queue.sort_by { |type, template_args| [template_args.size, type.name.size] }

    while !queue.empty?
        # If this is a template, we need to add the relevant
        # includes for the parameters as well. We need to do some
        # form of ordering for that to work ...
        queue.delete_if do |type, template_args|
            has_unresolved_args = template_args.any? do |template_arg_type|
                queue.include?(template_arg_type)
            end
            if !has_unresolved_args
                compute_orogen_include_on_type(type, file_to_include)
                true
            end
        end
    end
end

#resolve_toplevel_include_mapping(toplevel_files, options) ⇒ Object



1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
# File 'lib/orogen/gen/typekit.rb', line 1411

def resolve_toplevel_include_mapping(toplevel_files, options)
    preprocessed = Typelib::CXX.preprocess(toplevel_files, 'c', options)

    owners = Hash.new { |h,k| h[k] = Array.new }
    current_file = [[]]
    preprocessed.each_line do |line|
        if line =~ /# (\d+) "(.*)"(?: (\d))?/
            lineno, file, mode = Integer($1), $2, $3

            if mode == "1"
                toplevel_file =
                    if toplevel_files.include?(file) then file
                    else current_file.last[0]
                    end
                # the gccxml-importer always reported "flattened"
                # filepath, like "/usr/include/c++/4.9/bits".
                # clang++ does print
                # "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_vector.h"
                # into the preprocessed output (read here), so we
                # have to flattend the path. this can fail for
                # "built-in" for examples, thus the rescue.
                begin
                    current_file.push [toplevel_file, File.realpath(file), lineno]
                rescue Errno::ENOENT
                    # now file is smth like "<built-in>" or
                    # similar, at it anyways...
                    current_file.push [toplevel_file, file, lineno]
                end
            elsif mode == "2"
                current_file.pop
            end
            current_file.last[2] = lineno
        else
            toplevel, file, lineno = *current_file.last
            owners[file][lineno] = toplevel
            current_file.last[2] += 1
        end
    end
    return preprocessed, owners
end

#ro_ptr(name, options = Hash.new) ⇒ Object

Make the typekit define the specialization of RTT::ReadOnlyPointer for the given type.

See #smart_ptr for more information.



1013
1014
1015
1016
1017
# File 'lib/orogen/gen/typekit.rb', line 1013

def ro_ptr(name, options = Hash.new)
    options[:orogen_include] ||= Array.new
    options[:orogen_include] << "orocos-rtt-#{RTT_CPP.orocos_target}:rtt/extras/ReadOnlyPointer.hpp"
    smart_ptr("/RTT/extras/ReadOnlyPointer", find_type(name), options)
end

#ros_mappings(mappings) ⇒ Object



7
8
9
10
11
12
13
14
15
16
# File 'lib/orogen/marshallers/ros.rb', line 7

def ros_mappings(mappings)
    plugin = find_plugin('ros')
    if !plugin
        plugin = enable_plugin('ros')
        plugin.enabled = false
    end

    plugin.ros_mappings(mappings)
    self
end

#save_automatic(*args) ⇒ Object



1923
1924
1925
# File 'lib/orogen/gen/typekit.rb', line 1923

def save_automatic(*args)
    Generation.save_generated(true, automatic_dir, *args)
end

#save_automatic_public_header(*args) ⇒ Object



1917
1918
1919
1920
1921
# File 'lib/orogen/gen/typekit.rb', line 1917

def save_automatic_public_header(*args)
    rel = File.join(self.name, 'typekit', *args[0..-2])
    Generation.save_generated(true, automatic_dir, INCLUDE_DIR_NAME, rel, args[-1])
    rel
end

#save_user(*args) ⇒ Object



1927
1928
1929
1930
1931
1932
1933
# File 'lib/orogen/gen/typekit.rb', line 1927

def save_user(*args)
    path = Generation.save_generated(false, user_dir, *args)
    if templates_dir
        Generation.save_generated(true, templates_dir, *args)
    end
    path
end

#self_opaquesObject

Returns the set of opaques that have been added to this particular typekit



698
699
700
# File 'lib/orogen/gen/typekit.rb', line 698

def self_opaques
    opaques.find_all { |opdef| !imported_type?(opdef.type) }
end

#self_typenamesObject

Returns the set of type names defined in this typekit. This is different from self_types, as it returns a set of type names (i.e. strings), and also because it includes the aliases defined by the typekit.



1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
# File 'lib/orogen/gen/typekit.rb', line 1864

def self_typenames
    generated_types = []
    registry.each(:with_aliases => true) do |name, type|
        next if imported_type?(name)
        if !type.inlines_code?
            generated_types << name
        end
    end
    generated_types
end

#self_typesObject

Returns the set of types that are specifically handled by this typekit



1877
1878
1879
1880
1881
1882
1883
1884
# File 'lib/orogen/gen/typekit.rb', line 1877

def self_types
    generated_types = []
    registry.each do |type|
        next if imported_type?(type.name)
        generated_types << type
    end
    generated_types
end

#shared_ptr(name, options = Hash.new) ⇒ Object

Make the typekit define the specialization of boost::shared_ptr for the given type.

See #smart_ptr for more information.



1023
1024
1025
1026
1027
1028
1029
# File 'lib/orogen/gen/typekit.rb', line 1023

def shared_ptr(name, options = Hash.new)
    options[:orogen_include] ||= Array.new
    # actually we would need to add a pkg-config name in front of
    # the ":", but boost does _still_ not have pkg-config...
    options[:orogen_include] << ':boost/shared_ptr.hpp'
    smart_ptr("/boost/shared_ptr", find_type(name), options)
end

#smart_ptr(ptr_name, base_type, options = Hash.new) ⇒ Object

Ask to support a smart-pointer implementation on the given type. For instance, after the call

smart_ptr "boost::smart_ptr", "int"

You will be able to use the following type in your task definitions:

boost::smart_ptr_name<T>

The smart pointer class needs to meet the following requirements:

* the memory should be automatically managed (allocated/deallocated)
* the star operator can be used to get a const reference on the
  managed value (i.e. <tt>*pointer</tt> returns the object)
* the smart pointer must define a #reset method to set a new
  managed memory zone.

The Typekit#ro_ptr and Typekit#shared_ptr shortcuts are defined for boost::shared_ptr and RTT::ReadOnlyPointer.



956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
# File 'lib/orogen/gen/typekit.rb', line 956

def smart_ptr(ptr_name, base_type, options = Hash.new)
    typekit = self
    # create a validated options-hash
    options = Kernel.validate_options options,
        # just the plain name of the header like "path/Header.hpp"
        :include => [],
        # additional pkg-config info like "pkg-config-name:path/Header.hpp"
        :orogen_include => []
    # create opaque
    opaque = opaque_type("#{ptr_name}<#{base_type.name}>",
                         base_type,
                         options.merge(:needs_copy => false)) do |from, into|
        # create code from template
        Generation.render_template('typekit/smart_ptr.cpp', binding)
    end
    if base_type_inc = existing_orogen_include_for_type(base_type)
        base_type_inc.each do |or_inc|
            opaque..add('orogen_include', or_inc)
        end
    end

    # handle the "orogen_include" option, which has to contain the
    # pkg-config-name of the software providing this header
    options[:orogen_include].each do |or_inc|
        included_files << or_inc.split(':').last
        opaque..add('orogen_include', or_inc)
    end
    # Add the headers required for this smart pointer definition to
    # the list of included files. we dont have any pkg-config
    # informations in this channel, so just prepend a ":" to denote
    # the empty case.
    options[:include].each do |inc|
        included_files << inc
        # note that the "opaque_type()" factory-function used
        # previously to create the opaque for the ro_ptr will take
        # care that this header is added to the "orogen_include"
        # metadata as well
    end
    # ...and copy the header-informations needed for the type
    # verbatim.
    base_type..get('orogen_include').each do |or_inc|
        opaque..add('orogen_include', or_inc)
    end
    # Tell the port codegen that we must set a non-empty smart
    # pointer, or connections will fail.
    #
    # It's up to the user to set something more meaningful, but we
    # at least guarantee that things will work
    opaque..add 'orogen:cxx_port_codegen:constructor',
        "_%s.setDataSample(#{opaque.cxx_name}(new #{base_type.cxx_name}));"
    opaque
end

#standalone?Boolean

Returns:

  • (Boolean)


569
570
571
# File 'lib/orogen/gen/typekit.rb', line 569

def standalone?
    !project
end

#transport_plugin_name(transport_name) ⇒ Object



1935
1936
1937
# File 'lib/orogen/gen/typekit.rb', line 1935

def transport_plugin_name(transport_name)
    self.class.transport_plugin_name(transport_name, self.name)
end

#type_export_policyObject

:method: type_export_policy

call-seq:

type_export_policy new_policy => new_policy
type_export_policy => current_policy

Change or read the current type export policy. This policy drives what types orogen will import in the RTT type system.

If the :all policy is used (the default), then all types that have been imported through header files will be included. This can generate a lot of code, and therefore produce high compilation times and big typekit libraries.

If the :used policy is used, then will only be exported the types that are actually used in a task context of the typekit's project. This is most of the time a good idea.

If the :selected policy is used, then only types that have been explicitely selected with #export_types will be included.



658
659
660
661
662
663
664
665
666
667
668
669
# File 'lib/orogen/gen/typekit.rb', line 658

dsl_attribute :type_export_policy do |new_policy|
    new_policy = new_policy.to_sym
    if !TYPE_EXPORT_POLICIES.include?(new_policy)
        raise ArgumentError, "invalid type export policy #{new_policy.inspect}, allowed are: :#{TYPE_EXPORT_POLICIES.join(", :")}"
    end

    if new_policy == :used && !project
        raise ArgumentError, "cannot use a 'used' policy on a standalone typekit"
    end

    new_policy
end

#type_info_includes_for_type(type) ⇒ Object



2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
# File 'lib/orogen/gen/typekit.rb', line 2214

def type_info_includes_for_type(type)
    if type.opaque?
        return type_info_includes_for_type(intermediate_type_for(type))
    elsif TypekitMarshallers::TypeInfo::Plugin.rtt_scripting?
       result = ["#{self.name}/typekit/BoostSerialization.hpp", type.info_type_header]
       if type.full_name == "/std/string"
           result << "rtt/typekit/StdStringTypeInfo.hpp"
       end
       result
    else
       ["rtt/types/PrimitiveTypeInfo.hpp", "rtt/types/TemplateConnFactory.hpp"]
    end
end

#typekits_required_for(types) ⇒ Object

Computes the set of typekits that are required to get the given types.

self is never included in the result



1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
# File 'lib/orogen/gen/typekit.rb', line 1638

def typekits_required_for(types)
    result = Set.new
    types.each do |type|
        loop do
            imported_typekits.each do |tk|
                if type < Typelib::ArrayType 
                    if tk.defines_array_of?(type.deference)
                        result << tk
                    end
                elsif tk.includes?(type.name)
                    result << tk
                end
            end

            if type.respond_to?(:deference)
                type = type.deference
            else break
            end
        end
    end
    result
end

#used_typekitsObject

List of typekits that this typekit depends on



1662
1663
1664
1665
# File 'lib/orogen/gen/typekit.rb', line 1662

def used_typekits
    @registry = normalize_registry
    typekits_required_for(registry.each)
end

#using_library(library, options = Hash.new) ⇒ Object



717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/orogen/gen/typekit.rb', line 717

def using_library(library, options = Hash.new)
    if library.respond_to?(:to_str)
        library = Utilrb::PkgConfig.new(library)
    end

    options = Kernel.validate_options options, :link => true
    self.used_libraries << library
    self.include_dirs |= library.include_dirs.to_set
    if options[:link]
        self.linked_used_libraries << library
    end
end

#using_typekit(typekit) ⇒ Object



706
707
708
709
710
711
712
713
714
715
# File 'lib/orogen/gen/typekit.rb', line 706

def using_typekit(typekit)
    self.imported_types.merge(typekit.registry)
    self.imported_typelist |= typekit.typelist
    self.include_dirs      |= typekit.include_dirs.to_set
    self.opaques.concat(typekit.opaques)
    if dir = typekit.types_dir
        self.include_dirs << dir
    end
    self.imported_typekits << typekit
end


1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
# File 'lib/orogen/gen/typekit.rb', line 1545

def validate_related_types(registry)
     = lambda do |context, md, |
        md.get().each do |typename|
            if !registry.include?(typename)
                raise ArgumentError, "#{context} refers to #{typename} through the #{} metadata, but this type is not defined"
            end
        end
    end

    METADATA_RELATED_TYPES.each do ||
        registry.each do |type|
            .call(type.name, type., )
            if type.respond_to?(:field_metadata)
                type..each do |field_name, |
                    .call("#{type.name}.#{field_name}", , )
                end
            end
        end
    end
end

#xenomai?Boolean

True if the orocos target is xenomai

Returns:

  • (Boolean)


831
# File 'lib/orogen/gen/typekit.rb', line 831

def xenomai?; orocos_target == 'xenomai' end