module Mappings
Included Modules
- Mappings::Utils
Defined in:
Macro Summary
-
struct_array_mapping(class_name, of_class, struct_name, dbl_ptr = false, from_data = nil, from_c = nil, to_c = nil, size_field = size, data_field = data, size_type = nil)
Generates a class mapping a C structure representing an array of elements.
-
struct_mapping(class_name, struct_name, *args)
Generates a class mapping a C structure.
Macro Detail
Generates a class mapping a C structure representing an array of elements.
Overview
An array representation should contain the following fields:
- a pointer to the first element in the array.
- a number, the size of the array.
In practice, the created class is a wrapper around an Array holding the data.
initialize
methods are provided for instantiating the class from an array or a C structure
and to_unsafe
converts the data into a C structure representation.
Usage
Call struct_array_mapping
with:
- The class name that is going to be generated.
- The type of elements in the array.
- The mapped C structure.
- Additional arguements.
# Include the Mappings module
include Mappings
# Define dummy structures.
lib C
struct Data
int : LibC::Int
end
struct DataArray
data : Data*
size : Int32
end
end
# Map the structure to a crystal class.
struct_mapping Data, C::Data,
int : Int32
# Map the array structure to a crystal class.
struct_array_mapping DataArray, Data, C::DataArray
# The class can be instantiated from an array of crystal classes…
data_array = DataArray.new [
Data.new({int: 10}),
Data.new({int: 20}),
]
# or directly from the C structure.
data_array_from_c = DataArray.new C::DataArray.new(
size: 2,
data: [
C::Data.new(int: 10),
C::Data.new(int: 20),
]
)
# The two classes hold the same data.
pp data_array == data_array_from_c # => true
# And it can be converted back using the `to_unsafe` method.
pp data_array.to_unsafe
# => C::DataArray(
# @data=Pointer(C::Data)@0x1052c5e10,
# @size=2)
Additional arguments
- dbl_ptr (default: false)
Set dbl_ptr
to true if the c structure represents an array of structure pointers.
- size_field (default: size)
Used to identify the structure field name that contains the size of the array.
- data_field (default: data)
Used to identify the structure field name that contains the array pointer.
- from_data
Customize the behaviour when setting the internal data representation field from a crystal array.
- from_c
Customize the behaviour when setting the internal data representation field from a structure argument.
- to_c
Customize the behaviour when setting the C structure field from the internal data representation.
lib C
struct S
field : Int32
end
struct SArray
data : S*
size : LibC::Int
end
end
struct_mapping S, C::S, field : Int32
# Let's change the default beaviour using the `from_data`, `to_data` and `to_c` functions.
# For all these methods, `elt` is an element of the source array.
struct_array_mapping SArray, S, C::SArray,
# Here, we allow an SArray to be initialized with a plain array of integers.
from_data: S.new({field: elt}),
# Here, we substract one when initializing from a C structure.
from_c: S.new({field: elt.field - 1}),
# And we add one when converting into a C structure.
to_c: C::S.new field: elt.field + 1
pp SArray.new [0, 1, 2]
# => [{field: 0}, {field: 1}, {field: 2}]
pp SArray.new(
C::SArray.new size: 3, data: [
C::S.new(field: 1),
C::S.new(field: 2),
C::S.new(field: 3),
].to_unsafe
)
# => [{field: 0}, {field: 1}, {field: 2}]
pp SArray.new([0, 1, 2]).to_unsafe
# => C::SArray(@data=Pointer(C::S)@0x108cbfd60, @size=3)
Generates a class mapping a C structure.
Overview
In practice, the created class is a wrapper around a named tuple holding the data.
initialize
methods are provided for instantiating the class from a tuple or a C structure
and to_unsafe
converts the data into C struct representation.
The conversion is recursive, and will attempt to dereference structure pointers which is handy in case of nested structures.
Usage
Call struct_mapping
with:
- The class name that is going to be generated.
- The mapped C structure.
- The fields that the structure contains.
# Include the Mappings module
include Mappings
# Define a dummy structure.
lib C
struct Data
int : LibC::Int
end
end
# Map this structure to a crystal class.
struct_mapping Data, C::Data,
int : Int32
# The class can be instantiated from a named tuple containing the fields…
data = Data.new({int: 10})
# or directly from the C structure.
data_from_c = Data.new(
C::Data.new int: 10
)
# The two classes hold the same data.
pp data == data_from_c # => true
# And it can be converted back using the `to_unsafe` method.
pp data.to_unsafe # => C::Data(@int=10)
Field options
- ptr
Use ptr: true
to specify that the field contains a pointer to a nested structure.
lib C
struct S
# A nested structure
nested : N
# A pointer to a nested structure
nested_ptr : N*
end
struct N
int : LibC::Int
end
end
struct_mapping N, C::N, int : Int32
struct_mapping S, C::S,
nested : N,
# Declare this field as a pointer.
nested_ptr : N = {ptr: true}
s = S.new(C::S.new(
nested: C::N.new(int: 1),
nested_ptr: Pointer.malloc(sizeof(C::N), C::N.new int: 2)
))
# Both nested structures are converted into a nested named tuple.
pp s
# => {nested: {int: 1}, nested_ptr: {int: 2}}
# Here a pointer is allocated for the nested structure.
pp s.to_unsafe
# => C::S(@nested=C::N(@int=1), @nested_ptr=Pointer(C::N)@0x10fc64f20)
- from_data
Customize the behaviour when setting the internal data representation field from a tuple argument.
- from_c
Customize the behaviour when setting the internal data representation field from a structure argument.
- to_c
Customize the behaviour when setting the C structure field from the internal data representation.
lib C
struct S
field : Int32
end
end
struct_mapping S, C::S,
# Here, the crystal representation is a String, whereas the C representation is an integer.
# We are using the custom behaviour methods as adapters.
field : String = {
# data: the initialize function argument (named tuple)
from_data: data["field"].to_s,
# c_data: the initialize function argument (c structure)
from_c: c_data.field.to_s,
# [field name]: shortcut for @data["field name"], @data being the internal representation.
to_c: field.to_i32,
}
pp S.new({field: 20}) # {field: "20"}
pp S.new(C::S.new field: 20) # {field: "20"}
pp S.new({field: 20}).field # "20"
pp S.new({field: 20}).to_unsafe # C::S(@field=20)