nice-ffi 0.2 → 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog.txt +50 -0
- data/README.rdoc +5 -5
- data/docs/usage.rdoc +5 -2
- data/lib/nice-ffi/pathset.rb +6 -3
- data/lib/nice-ffi/struct.rb +12 -9
- data/pkg/nice-ffi-0.2/README.rdoc +88 -0
- data/pkg/nice-ffi-0.2/TODO.rdoc +16 -0
- data/pkg/nice-ffi-0.2/docs/usage.rdoc +361 -0
- metadata +5 -2
data/ChangeLog.txt
CHANGED
@@ -1,3 +1,53 @@
|
|
1
|
+
------------------------------------------------------------
|
2
|
+
Author: John Croisant <jacius@gmail.com>
|
3
|
+
Date: Sun Jan 17 21:04:08 2010 -0600
|
4
|
+
|
5
|
+
Nice-FFI 0.3 released.
|
6
|
+
|
7
|
+
------------------------------------------------------------
|
8
|
+
Author: John Croisant <jacius@gmail.com>
|
9
|
+
Date: Sun Jan 17 20:53:30 2010 -0600
|
10
|
+
|
11
|
+
Updated version and copyright dates.
|
12
|
+
|
13
|
+
M README.rdoc
|
14
|
+
M lib/nice-ffi/pathset.rb
|
15
|
+
M lib/nice-ffi/struct.rb
|
16
|
+
M nice-ffi.gemspec
|
17
|
+
|
18
|
+
------------------------------------------------------------
|
19
|
+
Author: John Croisant <jacius@gmail.com>
|
20
|
+
Date: Sun Jan 17 20:53:05 2010 -0600
|
21
|
+
|
22
|
+
Fixed AutoPointer errors in opaquestruct_spec.rb.
|
23
|
+
|
24
|
+
M spec/opaquestruct_spec.rb
|
25
|
+
|
26
|
+
------------------------------------------------------------
|
27
|
+
Author: John Croisant <jacius@gmail.com>
|
28
|
+
Date: Sun Jan 17 20:14:06 2010 -0600
|
29
|
+
|
30
|
+
Struct creates Buffer instead of MemoryPointer.
|
31
|
+
|
32
|
+
M lib/nice-ffi/struct.rb
|
33
|
+
|
34
|
+
------------------------------------------------------------
|
35
|
+
Author: John Croisant <jacius@gmail.com>
|
36
|
+
Date: Mon Jan 11 15:35:01 2010 -0600
|
37
|
+
|
38
|
+
Improved filename globs for Pathset::DEFAULT.
|
39
|
+
|
40
|
+
M docs/usage.rdoc
|
41
|
+
M lib/nice-ffi/pathset.rb
|
42
|
+
|
43
|
+
------------------------------------------------------------
|
44
|
+
Author: John Croisant <jacius@gmail.com>
|
45
|
+
Date: Thu Dec 10 13:50:24 2009 -0600
|
46
|
+
|
47
|
+
Updated Struct to play well with Buffers.
|
48
|
+
|
49
|
+
M lib/nice-ffi/struct.rb
|
50
|
+
|
1
51
|
------------------------------------------------------------
|
2
52
|
Author: John Croisant <jacius@gmail.com>
|
3
53
|
Date: Sat Oct 24 15:58:31 2009 -0500
|
data/README.rdoc
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
|
2
2
|
= Nice-FFI
|
3
3
|
|
4
|
-
Version:: 0.
|
5
|
-
Date::
|
4
|
+
Version:: 0.3
|
5
|
+
Date:: 2010-01-17
|
6
6
|
|
7
7
|
Homepage:: http://github.com/jacius/nice-ffi/
|
8
8
|
Author:: John Croisant <jacius@gmail.com>
|
9
|
-
Copyright:: 2009 John Croisant
|
9
|
+
Copyright:: 2009-2010 John Croisant
|
10
10
|
|
11
11
|
|
12
12
|
== Description
|
@@ -54,7 +54,7 @@ match the new API, then you should wait until version 1.0.
|
|
54
54
|
|
55
55
|
== Requirements
|
56
56
|
|
57
|
-
* Ruby-FFI >= 0.
|
57
|
+
* Ruby-FFI >= 0.5.0 (or compatible FFI implementation)
|
58
58
|
|
59
59
|
|
60
60
|
== Usage
|
@@ -66,7 +66,7 @@ See docs/usage.rdoc for usage information.
|
|
66
66
|
|
67
67
|
Nice-FFI is licensed under the following terms (the "MIT License"):
|
68
68
|
|
69
|
-
Copyright (c) 2009 John Croisant
|
69
|
+
Copyright (c) 2009-2010 John Croisant
|
70
70
|
|
71
71
|
Permission is hereby granted, free of charge, to any person obtaining
|
72
72
|
a copy of this software and associated documentation files (the
|
data/docs/usage.rdoc
CHANGED
@@ -62,12 +62,15 @@ library in likely directories. Specifically, it looks for:
|
|
62
62
|
}
|
63
63
|
|
64
64
|
files = {
|
65
|
-
/linux|bsd/ => [ "lib[NAME].so"
|
65
|
+
/linux|bsd/ => [ "lib[NAME].so*",
|
66
|
+
"lib[NAME]-*.so*" ],
|
66
67
|
|
67
68
|
/darwin/ => [ "lib[NAME].dylib",
|
69
|
+
"lib[NAME]-*.dylib",
|
68
70
|
"[NAME].framework/[NAME]" ],
|
69
71
|
|
70
|
-
/windows/ => [ "[NAME].dll"
|
72
|
+
/windows/ => [ "[NAME].dll",
|
73
|
+
"[NAME]-*.dll"]
|
71
74
|
}
|
72
75
|
|
73
76
|
NiceFFI::PathSet::DEFAULT = NiceFFI::PathSet.new( paths, files )
|
data/lib/nice-ffi/pathset.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
#
|
5
5
|
# Nice-FFI - Convenience layer atop Ruby-FFI
|
6
6
|
#
|
7
|
-
# Copyright (c) 2009 John Croisant
|
7
|
+
# Copyright (c) 2009-2010 John Croisant
|
8
8
|
#
|
9
9
|
# Permission is hereby granted, free of charge, to any person obtaining
|
10
10
|
# a copy of this software and associated documentation files (the
|
@@ -579,12 +579,15 @@ paths = {
|
|
579
579
|
}
|
580
580
|
|
581
581
|
files = {
|
582
|
-
/linux|bsd/ => [ "lib[NAME].so"
|
582
|
+
/linux|bsd/ => [ "lib[NAME].so*",
|
583
|
+
"lib[NAME]-*.so*" ],
|
583
584
|
|
584
585
|
/darwin/ => [ "lib[NAME].dylib",
|
586
|
+
"lib[NAME]-*.dylib",
|
585
587
|
"[NAME].framework/[NAME]" ],
|
586
588
|
|
587
|
-
/windows/ => [ "[NAME].dll"
|
589
|
+
/windows/ => [ "[NAME].dll",
|
590
|
+
"[NAME]-*.dll"]
|
588
591
|
}
|
589
592
|
|
590
593
|
# The default paths to look for libraries. See PathSet
|
data/lib/nice-ffi/struct.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
#
|
5
5
|
# Nice-FFI - Convenience layer atop Ruby-FFI
|
6
6
|
#
|
7
|
-
# Copyright (c) 2009 John Croisant
|
7
|
+
# Copyright (c) 2009-2010 John Croisant
|
8
8
|
#
|
9
9
|
# Permission is hereby granted, free of charge, to any person obtaining
|
10
10
|
# a copy of this software and associated documentation files (the
|
@@ -265,7 +265,7 @@ class NiceFFI::Struct < FFI::Struct
|
|
265
265
|
else
|
266
266
|
self.class_eval do
|
267
267
|
define_method( member ) do
|
268
|
-
return nil if self.pointer.null?
|
268
|
+
return nil if self.pointer.null? rescue NoMethodError
|
269
269
|
return self[member]
|
270
270
|
end
|
271
271
|
end
|
@@ -338,23 +338,23 @@ class NiceFFI::Struct < FFI::Struct
|
|
338
338
|
case val
|
339
339
|
|
340
340
|
when Hash
|
341
|
-
super()
|
341
|
+
super(FFI::Buffer.new(size))
|
342
342
|
init_from_hash( val ) # Read the values from a Hash.
|
343
343
|
|
344
344
|
# Note: plain "Array" would mean FFI::Struct::Array in this scope.
|
345
345
|
when ::Array
|
346
|
-
super()
|
346
|
+
super(FFI::Buffer.new(size))
|
347
347
|
init_from_array( val ) # Read the values from an Array.
|
348
348
|
|
349
349
|
when String
|
350
|
-
super()
|
350
|
+
super(FFI::Buffer.new(size))
|
351
351
|
init_from_bytes( val ) # Read the values from a bytestring.
|
352
352
|
|
353
353
|
when self.class
|
354
|
-
super()
|
354
|
+
super(FFI::Buffer.new(size))
|
355
355
|
init_from_bytes( val.to_bytes ) # Read the values from another instance.
|
356
356
|
|
357
|
-
when FFI::Pointer
|
357
|
+
when FFI::Pointer, FFI::Buffer
|
358
358
|
val = _make_autopointer( val, options[:autorelease] )
|
359
359
|
|
360
360
|
# Normal FFI::Struct behavior to wrap the pointer.
|
@@ -428,8 +428,11 @@ class NiceFFI::Struct < FFI::Struct
|
|
428
428
|
|
429
429
|
|
430
430
|
def to_s
|
431
|
-
|
432
|
-
|
431
|
+
begin
|
432
|
+
if self.pointer.null?
|
433
|
+
return "#<NULL %s:%#.x>"%[self.class.name, self.object_id]
|
434
|
+
end
|
435
|
+
rescue NoMethodError
|
433
436
|
end
|
434
437
|
|
435
438
|
mems = members.collect{ |m|
|
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
= Nice-FFI
|
3
|
+
|
4
|
+
Version:: 0.2
|
5
|
+
Date:: 2009-10-24
|
6
|
+
|
7
|
+
Homepage:: http://github.com/jacius/nice-ffi/
|
8
|
+
Author:: John Croisant <jacius@gmail.com>
|
9
|
+
Copyright:: 2009 John Croisant
|
10
|
+
|
11
|
+
|
12
|
+
== Description
|
13
|
+
|
14
|
+
Nice-FFI is a layer on top of Ruby-FFI [1] (and compatible FFI
|
15
|
+
systems) with features to ease development of FFI-based libraries.
|
16
|
+
|
17
|
+
Nice-FFI currently features:
|
18
|
+
|
19
|
+
* NiceFFI::Library: a stand-in for FFI::Library that provides methods
|
20
|
+
for easily finding and loading libraries on any platform, plus
|
21
|
+
automatic wrapping of functions that return struct pointers.
|
22
|
+
|
23
|
+
* NiceFFI::PathSet: a class with customizable rules for finding
|
24
|
+
library files on multiple operating system. PathSet is used by
|
25
|
+
NiceFFI::Library.load_library.
|
26
|
+
|
27
|
+
* NiceFFI::Struct: a stand-in for FFI::Struct that provides automatic
|
28
|
+
accessors for struct members, optional automatic memory management,
|
29
|
+
more instance initialization options, pretty to_s and inspect
|
30
|
+
methods, and other niceties.
|
31
|
+
|
32
|
+
* NiceFFI::OpaqueStruct: a base class for structs with no user-exposed
|
33
|
+
members. Useful when the struct definition is hidden by the
|
34
|
+
underlying C library.
|
35
|
+
|
36
|
+
Nice-FFI was originally developed as part of Ruby-SDL-FFI [2].
|
37
|
+
|
38
|
+
1. Ruby-FFI: http://github.com/ffi/ffi
|
39
|
+
2. Ruby-SDL-FFI: http://github.com/jacius/ruby-sdl-ffi/
|
40
|
+
|
41
|
+
|
42
|
+
== Caveats
|
43
|
+
|
44
|
+
Nice-FFI is still in EARLY DEVELOPMENT STAGES. That means:
|
45
|
+
|
46
|
+
* It may not work correctly (or at all).
|
47
|
+
* It may not be complete.
|
48
|
+
* It may change drastically with no advanced notice.
|
49
|
+
|
50
|
+
As such, this library is currently FOR THE ADVENTUROUS ONLY.
|
51
|
+
If you are not willing to continuously update your code to
|
52
|
+
match the new API, then you should wait until version 1.0.
|
53
|
+
|
54
|
+
|
55
|
+
== Requirements
|
56
|
+
|
57
|
+
* Ruby-FFI >= 0.4.0 (or compatible FFI implementation)
|
58
|
+
|
59
|
+
|
60
|
+
== Usage
|
61
|
+
|
62
|
+
See docs/usage.rdoc for usage information.
|
63
|
+
|
64
|
+
|
65
|
+
== License
|
66
|
+
|
67
|
+
Nice-FFI is licensed under the following terms (the "MIT License"):
|
68
|
+
|
69
|
+
Copyright (c) 2009 John Croisant
|
70
|
+
|
71
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
72
|
+
a copy of this software and associated documentation files (the
|
73
|
+
"Software"), to deal in the Software without restriction, including
|
74
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
75
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
76
|
+
permit persons to whom the Software is furnished to do so, subject to
|
77
|
+
the following conditions:
|
78
|
+
|
79
|
+
The above copyright notice and this permission notice shall be
|
80
|
+
included in all copies or substantial portions of the Software.
|
81
|
+
|
82
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
83
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
84
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
85
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
86
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
87
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
88
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
= TODO
|
3
|
+
|
4
|
+
* Struct
|
5
|
+
* Write accessor for nested structs (not struct pointers).
|
6
|
+
See comment in NiceFFI::Struct._make_writer for details.
|
7
|
+
|
8
|
+
* Specs
|
9
|
+
* Library (use a mock for ffi_lib.)
|
10
|
+
* PathSet#find (use a mock for File.exist?.)
|
11
|
+
* Struct
|
12
|
+
* TypedPointer
|
13
|
+
|
14
|
+
* Better documentation comments
|
15
|
+
* Library#attach_function
|
16
|
+
* Library#load_library
|
@@ -0,0 +1,361 @@
|
|
1
|
+
= Using Nice-FFI
|
2
|
+
|
3
|
+
This is a guide on how to use Nice-FFI's features. It assumes that you
|
4
|
+
are already somewhat familiar with Ruby-FFI.
|
5
|
+
|
6
|
+
|
7
|
+
== NiceFFI::Library
|
8
|
+
|
9
|
+
NiceFFI::Library is a drop-in replacement for FFI::Library. It provides
|
10
|
+
improved library finding abilities and support for TypedPointer return
|
11
|
+
types for attached functions.
|
12
|
+
|
13
|
+
|
14
|
+
In fact, NiceFFI::Library *is* FFI::Library, but with a few extras.
|
15
|
+
That means that you can do all the regular FFI::Library stuff as well
|
16
|
+
as the stuff described here.
|
17
|
+
|
18
|
+
|
19
|
+
=== load_library
|
20
|
+
|
21
|
+
NiceFFI::Library.load_library is a more convenient replacement for
|
22
|
+
FFI::Library.ffi_lib. It uses NiceFFI::PathSet to search for the
|
23
|
+
library in the most likely places, depending on the user's operating
|
24
|
+
system. For example, on Linux it would look for "lib[NAME].so" in
|
25
|
+
"/usr/lib/" (among others), while on Windows it would look for
|
26
|
+
"[NAME].dll" in "C:\windows\system32\".
|
27
|
+
|
28
|
+
Using load_library is easy. Just use "extend NiceFFI::Library" instead
|
29
|
+
of "extend FFI::Library", and use "load_library" instead of "ffi_lib":
|
30
|
+
|
31
|
+
|
32
|
+
require 'nice-ffi'
|
33
|
+
|
34
|
+
module MyLibraryModule
|
35
|
+
extend NiceFFI::Library
|
36
|
+
|
37
|
+
load_library("SDL") # look for libSDL.so, SDL.dll, etc.
|
38
|
+
|
39
|
+
# structs, functions, etc. as usual.
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
==== Advanced load_library
|
45
|
+
|
46
|
+
As mentioned, load_library uses NiceFFI::PathSet to search for the
|
47
|
+
library in likely directories. Specifically, it looks for:
|
48
|
+
|
49
|
+
|
50
|
+
paths = {
|
51
|
+
/linux|bsd/ => [ "/usr/local/lib/",
|
52
|
+
"/usr/lib/" ],
|
53
|
+
|
54
|
+
/darwin/ => [ "/usr/local/lib/",
|
55
|
+
"/sw/lib/",
|
56
|
+
"/opt/local/lib/",
|
57
|
+
"~/Library/Frameworks/",
|
58
|
+
"/Library/Frameworks/" ],
|
59
|
+
|
60
|
+
/windows/ => [ "C:\\windows\\system32\\",
|
61
|
+
"C:\\windows\\system\\" ]
|
62
|
+
}
|
63
|
+
|
64
|
+
files = {
|
65
|
+
/linux|bsd/ => [ "lib[NAME].so" ],
|
66
|
+
|
67
|
+
/darwin/ => [ "lib[NAME].dylib",
|
68
|
+
"[NAME].framework/[NAME]" ],
|
69
|
+
|
70
|
+
/windows/ => [ "[NAME].dll" ]
|
71
|
+
}
|
72
|
+
|
73
|
+
NiceFFI::PathSet::DEFAULT = NiceFFI::PathSet.new( paths, files )
|
74
|
+
|
75
|
+
|
76
|
+
The paths hash tells PathSet where to look for libraries, and the
|
77
|
+
files hash tells it the format of the library filename itself. The
|
78
|
+
string "[NAME]" is replaced with whatever string you pass to
|
79
|
+
load_library.
|
80
|
+
|
81
|
+
Each key in the hash should be a Regexp that matches an OS name from
|
82
|
+
FFI::Platform::OS. As of this writing (October 2009), the list of
|
83
|
+
recognized OS names is:
|
84
|
+
|
85
|
+
* "darwin" (MacOS X)
|
86
|
+
* "freebsd"
|
87
|
+
* "linux"
|
88
|
+
* "openbsd"
|
89
|
+
* "solaris"
|
90
|
+
* "windows"
|
91
|
+
|
92
|
+
So, if the user is running Linux and you try to load "SDL", it will
|
93
|
+
first look for "/usr/local/lib/libSDL.so". If it can't find that, it
|
94
|
+
will then look for "/usr/lib/libSDL.so". It would also use those same
|
95
|
+
paths for FreeBSD or OpenBSD, because those OS names also match the
|
96
|
+
regexp /linux|bsd/.
|
97
|
+
|
98
|
+
If the library could not be found in any of the given directories with
|
99
|
+
the given file name formats, load_library will just try loading "SDL"
|
100
|
+
using ffi_lib (which does some platform-appropriate guesses too). If
|
101
|
+
that fails too, LoadError is raised.
|
102
|
+
|
103
|
+
If you want to load from a different path, you can make a custom
|
104
|
+
PathSet and pass it to load_library:
|
105
|
+
|
106
|
+
|
107
|
+
libs_dir = File.dirname(__FILE__) + "/libs/"
|
108
|
+
|
109
|
+
pathset = NiceFFI::PathSet::DEFAULT.prepend( libs_dir )
|
110
|
+
|
111
|
+
load_library( "SDL", my_pathset )
|
112
|
+
|
113
|
+
|
114
|
+
The above example prepends (adds in front) the new paths so
|
115
|
+
that load_library will look for the library in "./libs/" first.
|
116
|
+
See PathSet for other useful methods for modifying PathSets.
|
117
|
+
|
118
|
+
|
119
|
+
Another advanced usage tip: If a library has several alternative
|
120
|
+
names, you can provide an Array of names:
|
121
|
+
|
122
|
+
|
123
|
+
# It might be called "foo", "foo2", or "Foo".
|
124
|
+
|
125
|
+
load_library( ["foo", "foo2", "Foo"] )
|
126
|
+
|
127
|
+
|
128
|
+
=== attach_function
|
129
|
+
|
130
|
+
NiceFFI::Library#attach_function behaves similarly to
|
131
|
+
FFI::Library#attach_function, except it supports TypedPointer return
|
132
|
+
values. For example, suppose you have a C function:
|
133
|
+
|
134
|
+
|
135
|
+
MyStruct *make_my_struct( int x, int y );
|
136
|
+
|
137
|
+
|
138
|
+
This returns a pointer to an instance of MyStruct. With FFI, you'd
|
139
|
+
write this to attach it:
|
140
|
+
|
141
|
+
|
142
|
+
attach_function :make_my_struct, [:int, :int], :pointer
|
143
|
+
|
144
|
+
|
145
|
+
And when you called it, it would return an FFI::Pointer, which you
|
146
|
+
would then have to manually wrap every time you called the method:
|
147
|
+
|
148
|
+
|
149
|
+
ptr = make_my_struct( 1, 2 )
|
150
|
+
mystruct = MyStruct.new( ptr )
|
151
|
+
|
152
|
+
|
153
|
+
With TypedPointer, the wrapping happens automatically. Just attach
|
154
|
+
the function with a TypedPointer instead of :pointer:
|
155
|
+
|
156
|
+
|
157
|
+
attach_function :make_my_struct, [:int, :int], NiceFFI::TypedPointer( MyStruct )
|
158
|
+
|
159
|
+
# If MyStruct is based on NiceFFI::Struct, you can do this instead:
|
160
|
+
|
161
|
+
attach_function :make_my_struct, [:int, :int], MyStruct.typed_pointer
|
162
|
+
|
163
|
+
|
164
|
+
Then you automatically get a MyStruct instance when you call the function:
|
165
|
+
|
166
|
+
|
167
|
+
mystruct = make_my_struct( 1, 2 )
|
168
|
+
mystruct.instance_of?( MyStruct ) # => Heck yeah it sure is!
|
169
|
+
|
170
|
+
|
171
|
+
Voila!
|
172
|
+
|
173
|
+
|
174
|
+
== NiceFFI::Struct
|
175
|
+
|
176
|
+
NiceFFI::Struct is a replacement for FFI::Struct. It provides several
|
177
|
+
features in addition to the normal FFI::Struct behavior:
|
178
|
+
|
179
|
+
* Ability to construct new instances from Array, Hash, another instance,
|
180
|
+
or a pointer as usual.
|
181
|
+
* Automatic read and write accessors for struct members.
|
182
|
+
* Accessors for struct pointer members with TypedPointer.
|
183
|
+
* Ability to dump an instance as an Array (#to_ary) or Hash (#to_hash).
|
184
|
+
* Pretty and useful #to_s and #inspect for debugging.
|
185
|
+
|
186
|
+
|
187
|
+
=== Constructors
|
188
|
+
|
189
|
+
NiceFFI::Struct allows you to construct a new struct instance from
|
190
|
+
a Hash, Array, or another existing instance of the same struct type.
|
191
|
+
It can also accept a pointer, just as with FFI::Struct.
|
192
|
+
|
193
|
+
|
194
|
+
class MyStruct < NiceFFI::Struct
|
195
|
+
layout :x, :int,
|
196
|
+
:y, :int
|
197
|
+
end
|
198
|
+
|
199
|
+
mystruct = MyStruct.new( {:x => 1, :y => 2} ) # from Hash
|
200
|
+
mystruct2 = MyStruct.new( [1,2] ) # from Array
|
201
|
+
mystruct3 = MyStruct.new( mystruct ) # from another instance
|
202
|
+
mystruct4 = MyStruct.new( ptr ) # from Pointer
|
203
|
+
|
204
|
+
|
205
|
+
=== Struct Member Accessors
|
206
|
+
|
207
|
+
Struct members are defined automatically when you use
|
208
|
+
NiceFFI::Struct.layout:
|
209
|
+
|
210
|
+
|
211
|
+
class MyStruct < NiceFFI::Struct
|
212
|
+
layout :x, :int,
|
213
|
+
:y, :int
|
214
|
+
end
|
215
|
+
|
216
|
+
mystruct = MyStruct.new({:x => 1, :y => 2})
|
217
|
+
|
218
|
+
mystruct.x # => 1
|
219
|
+
mystruct.y # => 2
|
220
|
+
|
221
|
+
mystruct.x = 3
|
222
|
+
mystruct.y = -4
|
223
|
+
|
224
|
+
|
225
|
+
Sometimes a struct will have members that should be read-only,
|
226
|
+
or completely hidden. In those cases, you can use
|
227
|
+
NiceFFI::Struct.read_only and NiceFFI::Struct.hidden.
|
228
|
+
|
229
|
+
|
230
|
+
class MySneakyStruct < NiceFFI::Struct
|
231
|
+
layout :readme, :int,
|
232
|
+
:readme2, :int,
|
233
|
+
:hideme, :pointer,
|
234
|
+
:hideme2, :pointer,
|
235
|
+
:normal, :uint32
|
236
|
+
|
237
|
+
read_only :readme, :readme2
|
238
|
+
hidden :hideme, :hideme2
|
239
|
+
end
|
240
|
+
|
241
|
+
sneaky = MySneakyStruct.new( ... )
|
242
|
+
|
243
|
+
|
244
|
+
read_only prevents a write accessor from being created (or removes
|
245
|
+
it if there is already one). hidden does the same, but for both
|
246
|
+
read and write accessors. hidden also prevents the member from
|
247
|
+
being shown in #to_s and #inspect.
|
248
|
+
|
249
|
+
read_only and hidden can go before or after layout (or both),
|
250
|
+
and you can safely call them multiple times if you need to.
|
251
|
+
|
252
|
+
|
253
|
+
=== TypedPointer Struct Member Accessors
|
254
|
+
|
255
|
+
Some struct members are :pointers that point to other structs.
|
256
|
+
With FFI::Struct, you'd have to manually wrap and unwrap the
|
257
|
+
struct pointer, but if you specify a TypedPointer instead of
|
258
|
+
:pointer, NiceFFI::Struct will wrap and unwrap it automatically:
|
259
|
+
|
260
|
+
|
261
|
+
class StructWithPtr < NiceFFI::Struct
|
262
|
+
layout :x, :int,
|
263
|
+
:y, :int,
|
264
|
+
:my, NiceFFI::TypedPointer( MyStruct )
|
265
|
+
end
|
266
|
+
|
267
|
+
struct = StructWithPtr.new( :x => -1,
|
268
|
+
:y => -2,
|
269
|
+
:my => MyStruct.new([1,2]) )
|
270
|
+
|
271
|
+
# Seamlessly wraps the pointer in a struct
|
272
|
+
struct.my.kind_of? MyStruct # true
|
273
|
+
|
274
|
+
# Seamlessly unwraps the struct and stores the pointer
|
275
|
+
struct.my = MyStruct.new([-4,-3])
|
276
|
+
|
277
|
+
|
278
|
+
=== Automatic Memory Managment
|
279
|
+
|
280
|
+
Ruby-FFI already provides automatic memory management when you create
|
281
|
+
a FFI::MemoryPointer or FFI::Buffer instance. When those instances are
|
282
|
+
garbage collected, their memory is automatically released so it can be
|
283
|
+
used elsewhere.
|
284
|
+
|
285
|
+
That feature is used by NiceFFI::Struct when you create a new instance
|
286
|
+
by passing a Hash, Array, String, or another instance. In those cases,
|
287
|
+
new memory is allocated for the struct instance, and automatically
|
288
|
+
released when the struct instance is galbage collected.
|
289
|
+
|
290
|
+
NiceFFI::Struct also provides an optional automatic memory management
|
291
|
+
system for normal pointers. To use this system, define a "release"
|
292
|
+
class method in your class. Then if you create a new struct instance
|
293
|
+
with an FFI::Pointer, the release class method will automatically be
|
294
|
+
called when the memory for a struct instance needs to be freed.
|
295
|
+
|
296
|
+
(This also applies to attached functions with TypedPointer return
|
297
|
+
values. The pointers returned from those functions are wrapped in the
|
298
|
+
struct class, so if you have defined the release class method, they
|
299
|
+
will be automatically memory managed.)
|
300
|
+
|
301
|
+
The release class method must accept an FFI::Pointer and call an
|
302
|
+
appropriate function to free the struct's memory. Here's an example
|
303
|
+
from Ruby-SDL-FFI:
|
304
|
+
|
305
|
+
|
306
|
+
class Surface < NiceFFI::Struct
|
307
|
+
|
308
|
+
def self.release( pointer )
|
309
|
+
SDL.FreeSurface( pointer )
|
310
|
+
end
|
311
|
+
|
312
|
+
# ...
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
Note: the release class method should not have any side effects
|
318
|
+
besides freeing the struct's memory. Don't be clever!
|
319
|
+
|
320
|
+
The memory management system keeps a reference count for each pointer
|
321
|
+
address, so the release class method will only be called when all
|
322
|
+
struct instances that are using that memory have been garbage
|
323
|
+
collected. That means it's safe to have many instances sharing the
|
324
|
+
same memory.
|
325
|
+
|
326
|
+
If you want to create an instance that doesn't use the memory
|
327
|
+
management system, you can disable the :autorelease option when
|
328
|
+
creating the instance, like so:
|
329
|
+
|
330
|
+
|
331
|
+
struct = MyStructClass.new( a_pointer, :autorelease => false )
|
332
|
+
|
333
|
+
|
334
|
+
== NiceFFI::OpaqueStruct
|
335
|
+
|
336
|
+
Some C libraries have structs with no publicly-visible layout.
|
337
|
+
Instead, the internal details are hidden, and only modified by calling
|
338
|
+
functions in the library.
|
339
|
+
|
340
|
+
For example, the SDL_mixer library has this definition in its header
|
341
|
+
file:
|
342
|
+
|
343
|
+
|
344
|
+
typedef struct _Mix_Music Mix_Music;
|
345
|
+
|
346
|
+
|
347
|
+
"_Mix_Music" is a struct that is defined within SDL_mixer, but its
|
348
|
+
internals are different depending on what features SDL_mixer was
|
349
|
+
compiled with. The struct members are not revealed in the header file,
|
350
|
+
so they can't be accessed like a normal struct.
|
351
|
+
|
352
|
+
NiceFFI provides a class for handling special cases like this,
|
353
|
+
NiceFFI::OpaqueStruct. OpaqueStruct has no layout and no members, and
|
354
|
+
cannot be created by passing in Hashes, Arrays, etc. It simply holds a
|
355
|
+
pointer to the struct memory. As with NiceStruct (and FFI::Struct),
|
356
|
+
instances of OpaqueStruct-based classes can be passed directly to
|
357
|
+
functions expecting a pointer of the appropriate struct type.
|
358
|
+
|
359
|
+
OpaqueStruct features the same optional memory management system as
|
360
|
+
NiceStruct. Read the "Automatic Memory Management" section above for
|
361
|
+
information about how to use this feature.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nice-ffi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.3"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Croisant
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-17 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -34,6 +34,9 @@ extensions: []
|
|
34
34
|
extra_rdoc_files: []
|
35
35
|
|
36
36
|
files:
|
37
|
+
- pkg/nice-ffi-0.2/docs/usage.rdoc
|
38
|
+
- pkg/nice-ffi-0.2/TODO.rdoc
|
39
|
+
- pkg/nice-ffi-0.2/README.rdoc
|
37
40
|
- docs/usage.rdoc
|
38
41
|
- TODO.rdoc
|
39
42
|
- README.rdoc
|