Skip to content

Commit 7835410

Browse files
authored
Merge pull request #160 from kornilova-l/constructors
Generate constructor helper
2 parents fa784c1 + 27a5643 commit 7835410

File tree

23 files changed

+643
-142
lines changed

23 files changed

+643
-142
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,35 @@ object vector {
5353
def y: native.CFloat = !p._2
5454
def y_=(value: native.CFloat): Unit = !p._2 = value
5555
}
56-
def struct_point()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point]
5756
5857
implicit class struct_vector_ops(val p: native.Ptr[struct_vector]) extends AnyVal {
5958
def a: native.Ptr[struct_point] = p._1
6059
def a_=(value: native.Ptr[struct_point]): Unit = !p._1 = !value
6160
def b: native.Ptr[struct_point] = p._2
6261
def b_=(value: native.Ptr[struct_point]): Unit = !p._2 = !value
6362
}
64-
def struct_vector()(implicit z: native.Zone): native.Ptr[struct_vector] = native.alloc[struct_vector]
63+
}
64+
65+
object struct_point {
66+
import implicits._
67+
def apply()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point]
68+
def apply(x: native.CFloat, y: native.CFloat)(implicit z: native.Zone): native.Ptr[struct_point] = {
69+
val ptr = native.alloc[struct_point]
70+
ptr.x = x
71+
ptr.y = y
72+
ptr
73+
}
74+
}
75+
76+
object struct_vector {
77+
import implicits._
78+
def apply()(implicit z: native.Zone): native.Ptr[struct_vector] = native.alloc[struct_vector]
79+
def apply(a: native.Ptr[struct_point], b: native.Ptr[struct_point])(implicit z: native.Zone): native.Ptr[struct_vector] = {
80+
val ptr = native.alloc[struct_vector]
81+
ptr.a = a
82+
ptr.b = b
83+
ptr
84+
}
6585
}
6686
}
6787
```

bindgen/ir/IR.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,13 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) {
181181
s << "\n object implicits {\n" << ir.getHelperMethods() << " }\n";
182182
}
183183

184+
for (const auto &str : ir.structs) {
185+
visitedTypes.clear();
186+
if (ir.shouldOutput(str, visitedTypes) && str->hasHelperMethods()) {
187+
s << "\n" << str->getConstructorHelper(ir.locationManager);
188+
}
189+
}
190+
184191
s << "}\n\n";
185192

186193
return s;

bindgen/ir/Struct.cpp

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@ Struct::generateHelperClass(const LocationManager &locationManager) const {
4444
}
4545
s << " }\n";
4646

47-
/* makes struct instantiation easier */
48-
s << " def "
49-
<< type + "()(implicit z: native.Zone): native.Ptr[" + type + "]"
50-
<< " = native.alloc[" + type + "]\n";
51-
5247
return s.str();
5348
}
5449

@@ -132,7 +127,8 @@ std::string Struct::generateSetterForStructRepresentation(
132127
unsigned fieldIndex, const LocationManager &locationManager) const {
133128
std::shared_ptr<Field> field = fields[fieldIndex];
134129
std::string setter = handleReservedWords(field->getName(), "_=");
135-
std::string parameterType = field->getType()->str(locationManager);
130+
std::string parameterType =
131+
wrapArrayOrRecordInPointer(field->getType())->str(locationManager);
136132
std::string value = "value";
137133
std::vector<std::shared_ptr<const Struct>> structTypesThatShouldBeReplaced =
138134
shouldFieldBreakCycle(field);
@@ -141,9 +137,7 @@ std::string Struct::generateSetterForStructRepresentation(
141137
std::shared_ptr<const Type> typeReplacement = getTypeReplacement(
142138
field->getType(), structTypesThatShouldBeReplaced);
143139
value = value + ".cast[" + typeReplacement->str(locationManager) + "]";
144-
} else if (isAliasForType<ArrayType>(field->getType().get()) ||
145-
isAliasForType<Struct>(field->getType().get())) {
146-
parameterType = "native.Ptr[" + parameterType + "]";
140+
} else if (isArrayOrRecord(field->getType())) {
147141
value = "!" + value;
148142
}
149143
std::stringstream s;
@@ -156,17 +150,16 @@ std::string Struct::generateGetterForStructRepresentation(
156150
unsigned fieldIndex, const LocationManager &locationManager) const {
157151
std::shared_ptr<Field> field = fields[fieldIndex];
158152
std::string getter = handleReservedWords(field->getName());
159-
std::string returnType = field->getType()->str(locationManager);
153+
std::string returnType =
154+
wrapArrayOrRecordInPointer(field->getType())->str(locationManager);
160155
std::string methodBody = "p._" + std::to_string(fieldIndex + 1);
161-
if (isAliasForType<ArrayType>(field->getType().get()) ||
162-
isAliasForType<Struct>(field->getType().get())) {
163-
returnType = "native.Ptr[" + returnType + "]";
164-
} else if (!shouldFieldBreakCycle(field).empty()) {
165-
/* field type is changed to avoid cyclic types in generated code */
166-
methodBody = "(!" + methodBody + ").cast[" +
167-
field->getType()->str(locationManager) + "]";
168-
} else {
156+
if (!isArrayOrRecord(field->getType())) {
169157
methodBody = "!" + methodBody;
158+
if (!shouldFieldBreakCycle(field).empty()) {
159+
/* field type is changed to avoid cyclic types in generated code */
160+
methodBody = "(" + methodBody + ").cast[" +
161+
field->getType()->str(locationManager) + "]";
162+
}
170163
}
171164
std::stringstream s;
172165
s << " def " << getter << ": " << returnType << " = " << methodBody
@@ -182,7 +175,8 @@ std::string Struct::generateSetterForArrayRepresentation(
182175
unsigned int fieldIndex, const LocationManager &locationManager) const {
183176
std::shared_ptr<Field> field = fields[fieldIndex];
184177
std::string setter = handleReservedWords(field->getName(), "_=");
185-
std::string parameterType = field->getType()->str(locationManager);
178+
std::string parameterType =
179+
wrapArrayOrRecordInPointer(field->getType())->str(locationManager);
186180
std::string value = "value";
187181
std::string castedField = "p._1";
188182

@@ -201,9 +195,7 @@ std::string Struct::generateSetterForArrayRepresentation(
201195
std::shared_ptr<const Type> typeReplacement = getTypeReplacement(
202196
field->getType(), structTypesThatShouldBeReplaced);
203197
value = value + ".cast[" + typeReplacement->str(locationManager) + "]";
204-
} else if (isAliasForType<ArrayType>(field->getType().get()) ||
205-
isAliasForType<Struct>(field->getType().get())) {
206-
parameterType = pointerToFieldType.str(locationManager);
198+
} else if (isArrayOrRecord(field->getType())) {
207199
value = "!" + value;
208200
}
209201
std::stringstream s;
@@ -217,7 +209,8 @@ std::string Struct::generateGetterForArrayRepresentation(
217209
unsigned fieldIndex, const LocationManager &locationManager) const {
218210
std::shared_ptr<Field> field = fields[fieldIndex];
219211
std::string getter = handleReservedWords(field->getName());
220-
std::string returnType;
212+
std::string returnType =
213+
wrapArrayOrRecordInPointer(field->getType())->str(locationManager);
221214
std::string methodBody;
222215

223216
PointerType pointerToFieldType = PointerType(field->getType());
@@ -230,17 +223,13 @@ std::string Struct::generateGetterForArrayRepresentation(
230223
methodBody =
231224
methodBody + ".cast[" + pointerToFieldType.str(locationManager) + "]";
232225

233-
if (isAliasForType<ArrayType>(field->getType().get()) ||
234-
isAliasForType<Struct>(field->getType().get())) {
235-
returnType = pointerToFieldType.str(locationManager);
236-
} else if (!shouldFieldBreakCycle(field).empty()) {
237-
/* field type is changed to avoid cyclic types in generated code */
238-
methodBody = "(!" + methodBody + ").cast[" +
239-
field->getType()->str(locationManager) + "]";
240-
returnType = field->getType()->str(locationManager);
241-
} else {
226+
if (!isArrayOrRecord(field->getType())) {
242227
methodBody = "!" + methodBody;
243-
returnType = field->getType()->str(locationManager);
228+
if (!shouldFieldBreakCycle(field).empty()) {
229+
/* field type is changed to avoid cyclic types in generated code */
230+
methodBody = "(" + methodBody + ").cast[" +
231+
field->getType()->str(locationManager) + "]";
232+
}
244233
}
245234
std::stringstream s;
246235
s << " def " << getter << ": " << returnType << " = " << methodBody
@@ -352,3 +341,47 @@ bool Struct::hasBiggestName(const CycleNode &node,
352341
}
353342
return false;
354343
}
344+
345+
std::string
346+
Struct::getConstructorHelper(const LocationManager &locationManager) const {
347+
std::stringstream s;
348+
std::string type = replaceChar(getTypeName(), " ", "_");
349+
s << " object " << type << " {\n"
350+
<< " import implicits._\n";
351+
352+
/* constructor with no parameters */
353+
s << " def apply()(implicit z: native.Zone): native.Ptr[" + type + "]"
354+
<< " = native.alloc[" + type + "]\n";
355+
356+
/* constructor that initializes all fields */
357+
s << " def apply(";
358+
std::string sep = "";
359+
for (const auto &field : fields) {
360+
s << sep << handleReservedWords(field->getName()) << ": "
361+
<< wrapArrayOrRecordInPointer(field->getType())->str(locationManager);
362+
sep = ", ";
363+
}
364+
s << ")(implicit z: native.Zone): native.Ptr[" << type << "] = {\n"
365+
<< " val ptr = native.alloc[" << type << "]\n";
366+
for (const auto &field : fields) {
367+
std::string name = handleReservedWords(field->getName());
368+
s << " ptr." << name << " = " << name << "\n";
369+
}
370+
s << " ptr\n"
371+
<< " }\n"
372+
<< " }\n";
373+
return s.str();
374+
}
375+
376+
bool Struct::isArrayOrRecord(std::shared_ptr<const Type> type) const {
377+
return isAliasForType<ArrayType>(type.get()) ||
378+
isAliasForType<Struct>(type.get());
379+
}
380+
381+
std::shared_ptr<const Type>
382+
Struct::wrapArrayOrRecordInPointer(std::shared_ptr<const Type> type) const {
383+
if (isArrayOrRecord(type)) {
384+
return std::make_shared<PointerType>(type);
385+
}
386+
return type;
387+
}

bindgen/ir/Struct.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class Struct : public Record {
4141
const std::shared_ptr<const Struct> &startStruct, CycleNode &cycleNode,
4242
std::vector<std::shared_ptr<const Type>> &visitedTypes) const override;
4343

44+
std::string
45+
getConstructorHelper(const LocationManager &locationManager) const;
46+
4447
private:
4548
/** type size is needed if number of fields is bigger than 22 */
4649
uint64_t typeSize;
@@ -121,6 +124,14 @@ class Struct : public Record {
121124
*/
122125
bool hasBiggestName(const CycleNode &node,
123126
std::vector<std::string> namesInCycle) const;
127+
128+
bool isArrayOrRecord(std::shared_ptr<const Type> type) const;
129+
130+
/**
131+
* @return pointer to given type if given type is record or array
132+
*/
133+
std::shared_ptr<const Type>
134+
wrapArrayOrRecordInPointer(std::shared_ptr<const Type> type) const;
124135
};
125136

126137
#endif // SCALA_NATIVE_BINDGEN_STRUCT_H

build.sbt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ lazy val docs = project("docs")
146146
.link("vector")
147147
.packageName("org.example")
148148
},
149+
nativeBindgenPath := {
150+
Some(
151+
(ThisBuild / baseDirectory).value / "bindgen/target/scala-native-bindgen")
152+
},
149153
Test / nativeBindgen / target := (Test / scalaSource).value / "org/example",
150154
nativeLinkStubs := true,
151155
compileTask("vector", Test / resourceDirectory),

docs/src/paradox/using-generated-bindings.md

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,52 @@ Consider following header file:
44

55
@@snip [vector.h] (../test/resources/vector.h)
66

7-
Bindgen will generate type aliases for the structs, binding for function `add`
8-
and helper functions that make usage of structs easier.
7+
Bindgen will output
8+
* type aliases for the structs
9+
* binding for function `cosine`
10+
* helper functions that make usage of structs easier
911

1012
@@snip [vector.h] (../test/scala/org/example/vector.scala)
1113

12-
Let's write code that creates two vectors, adds them and prints resulting
13-
vector.
14+
Let's write code that creates two line segments and prints angel between them.
1415

15-
First we need to create points for vectors. We will use `native.Zone` to
16+
First we need to create points. We will use `native.Zone` to
1617
allocate struct (more information on memory management can be found
1718
here: [Scala Native memory management]).
1819

19-
Helper object `mylibHelpers` contains function for struct allocation.
20-
To import it use `import mylibHelpers._`
20+
Generated bindings contain helper functions that make struct allocation easier.
21+
To import them use `import org.example.vector._`
2122

22-
Let's create points for first vector:
23+
Let's create two points and the first line segment:
2324

2425
@@snip [step-1] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-1 }
2526

26-
Now we want to set fields of created points. Scala Native provides access
27-
to fields by using `_N` methods where `N` is index of a field
28-
(see [Scala Native memory layout types]).
27+
There is no need to create points manually, just call `struct_lineSegment`
28+
constructor and set point coordinates using fields setters.
2929

30-
Bindgen generates implicit helper classes that wrap calls to `_N` in functions
31-
with meaningful names. We already imported helper class, so we can use the
32-
functions:
30+
Scala Native allows us to access a field by using `_N` method where `N` is index of a field
31+
(see [Scala Native memory layout types]) but it is not convenient because we have to
32+
match indices with fields names.
33+
34+
Bindgen provides implicit helper classes that wrap calls to `_N` in functions
35+
with meaningful names. To import these classes add `import org.example.vector.implicits._`
36+
to your code:
3337

3438
@@snip [step-2] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-2 }
3539

36-
Lets create first vector. Note that `struct_vector` contains
37-
fields of type `struct_point` but setters accept variables of type
38-
`native.Ptr[struct_point]`. It helps to avoid Scala Native limitation that
39-
does not allow passing structs and arrays by value
40-
(see @github[scala-native/scala-native#555](scala-native/scala-native#555)).
40+
@@@ note
41+
42+
Note that `struct_lineSegment` contains fields of value type `struct_point`
43+
but setters accept variables of type `native.Ptr[struct_point]`.
44+
It helps to avoid Scala Native limitation that does not allow passing structs
45+
and arrays by value (see @github[scala-native/scala-native#555](scala-native/scala-native#555)).
46+
47+
@@@
4148

42-
@@snip [step-3] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-3 }
49+
Now we can calculate angel between line segments:
4350

44-
Repeat these steps to create second vector. Once both vectors are created we can
45-
call `add` function and print the result:
51+
@@snip [step-3] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-2 }
4652

47-
@@snip [step-4] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-4 }
4853

4954
[Scala Native memory management]: http://www.scala-native.org/en/latest/user/interop.html#memory-management
5055
[Scala Native memory layout types]: http://www.scala-native.org/en/latest/user/interop.html#memory-layout-types

docs/src/test/resources/vector.c

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
#include "vector.h"
2+
#include <math.h>
23
#include <stdlib.h>
34

4-
struct vector *add(struct vector *v1, struct vector *v2) {
5-
struct vector *out = malloc(sizeof(struct vector));
6-
7-
if (!out)
8-
return NULL;
9-
10-
out->a.x = v1->a.x + v2->a.x;
11-
out->a.y = v1->a.y + v2->a.y;
12-
out->b.x = v1->b.x + v2->b.x;
13-
out->b.y = v1->b.y + v2->b.y;
14-
15-
return out;
16-
}
5+
float cosine(struct lineSegment *v1, struct lineSegment *v2) {
6+
float v1x = fabsf(v1->b.x - v1->a.x);
7+
float v1y = fabsf(v1->b.y - v1->a.y);
8+
float v1Length = sqrtf(v1x * v1x + v1y * v1y);
9+
float v2x = fabsf(v2->b.x - v2->a.x);
10+
float v2y = fabsf(v2->b.y - v2->a.y);
11+
float v2Length = sqrtf(v2x * v2x + v2y * v2y);
12+
return (v1x * v2x + v1y * v2y) / (v1Length * v2Length);
13+
}

docs/src/test/resources/vector.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ struct point {
33
float y;
44
};
55

6-
struct vector {
6+
struct lineSegment {
77
struct point a;
88
struct point b;
99
};
1010

11-
struct vector *add(struct vector *v1, struct vector *v2);
11+
float cosine(struct lineSegment *v1, struct lineSegment *v2);

0 commit comments

Comments
 (0)