Skip to content

elbywan/struct-mappings

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

struct-mappings

Generate crystal classes mapping C structures.

Docs GitHub release Build Status

Purpose

struct-mappings exposes useful macros that can be used to convert a C struct into a Crystal class and vice versa.

The inner representation of the data is a NamedTuple which is very convenient to use.

Utility

Conversion from & to a C structure could be useful in the following scenarios:

  • Persisting data that would get freed otherwise when calling an external library.
  • Avoiding calling .value on nested structure pointers.
  • C structs are arguably less convenient to use compared to named tuples.

Features

  • Supports nested structures.
  • Automatically allocates & dereference structure pointers.
  • C structures containing array pointers can be converted to an array wrapper class.
  • Conversion can be customized.

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      struct-mappings:
        github: elbywan/struct-mappings
  2. Run shards install

Usage

Require

require "struct-mappings"

Minimal use case

# 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)

# For structures that represent arrays, it's also quite simple.

# Define a dummy array.
lib C
  struct DataArray
    data: Data*
    size: LibC::Int
  end
end

# Map the array to a crystal class.
# Generates a `DataArray` class which is a wrapper around
# an Array of `Data` classes, and mapping the `C::DataArray` struct.
struct_array_mapping DataArray, Data, C::DataArray

# Can be instantiated from a crystal array…
array_from_data = DataArray.new [
  Data.new({int: 10}),
  Data.new({int: 20}),
]
pp array_from_data # => [{int: 10}, {int: 20}]

# or a C structure representation.
array_from_c = DataArray.new C::DataArray.new(
  size: 2,
  data: [
    C::Data.new(int: 10),
    C::Data.new(int: 20),
  ].to_unsafe
)
pp array_from_c # => [{int: 10}, {int: 20}]

# The two classes hold the same data.
pp array_from_data == array_from_c # => true

# And it can be converted back using the `to_unsafe` method.
pp array_from_data.to_unsafe # => C::DataArray(@data=Pointer(C::Data)@0x10e1cfda0, @size=2)

Documentation

The exhaustive documentation is hosted here.

Contributing

  1. Fork it (https://github.com/your-github-user/struct-mappings/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors