Open HertzDevil opened 3 years ago
In general, I really like these macros. They're much nicer to use than those mumbo-jumbo generated ones! However:
def f(pen : Qt::Pen)
# before
pen.dash_pattern = [3.0, 1.5, 1.0, 0.5]
# after
pen.dash_pattern = Qt::QVector.of(Float64).from [3.0, 1.5, 1.0, 0.5]
end
I think the 'after' usage is too complicated. Bindgen strives to produce code that's nice to use to the lib-user (At the expense of ease-of-use as configuration goes at times).
Understandable. That said, the breaking change only happens when passing containers to the wrapper; the wrapper should be free to return containers directly, i.e.
class Pen
# before
def dash_pattern : Enumerable(Float64)
# after
def dash_pattern : QVector(Float64)
end
This might be sufficient to fix the second issue. For it not to break existing code QVector(Float64) < Enumerable(Float64)
must be true, but this isn't the case right now because Indexable(Float64)
comes from BindgenHelper::SequentialContainer(Float64)
instead, and namespaces (modules) cannot include other modules at the moment. So I'll leave this part as is and the patch is ready.
QVector.of
will look like this after this patch is rebased onto #108:
module QVector(T)
macro of(*type_args)
{% types = type_args.map(&.resolve) %}
{% if types == {UInt32} %} {{ Container_QVector_unsigned_int_ }}
{% elsif types == {Point} %} {{ Container_QVector_QPoint_ }}
{% elsif types == {PointF} %} {{ Container_QVector_QPointF_ }}
{% elsif types == {Float64} %} {{ Container_QVector_double_ }}
{% elsif types == {TextLength} %} {{ Container_QVector_QTextLength_ }}
{% elsif types == {TextFormat} %} {{ Container_QVector_QTextFormat_ }}
{% elsif types == {LineF} %} {{ Container_QVector_QLineF_ }}
{% elsif types == {Line} %} {{ Container_QVector_QLine_ }}
{% elsif types == {RectF} %} {{ Container_QVector_QRectF_ }}
{% elsif types == {Rect} %} {{ Container_QVector_QRect_ }}
{% else %} {% raise "QVector(#{types.splat}) has not been instantiated" %}
{% end %}
end
end
Rebased on top of master. Also there is now special logic to prefer container module names (TypeConfig#container_type
) over #crystal_type
, so nested container names can be disambiguated in the .of
macro without the pass behaviour of any containers being altered (see last commit for examples).
Please take a look and see if anything still needs to be changed.
This might be sufficient to fix the second issue. For it not to break existing code QVector(Float64) < Enumerable(Float64) must be true, but this isn't the case right now because Indexable(Float64) comes from BindgenHelper::SequentialContainer(Float64) instead, and namespaces (modules) cannot include other modules at the moment. So I'll leave this part as is and the patch is ready.
Couldn't this be done if each specialization would include these modules by themselves?
More specifically it could break code that depends on that return type for type deduction, e.g. instance variables: https://play.crystal-lang.org/#/r/9z1p
If T.f
returns an Enumerable(Int32)
then any of the include
s will work, but if it returns Vector(Int32)
then only the one include
inside Vector
itself will work, because T#@x
's type is deduced to be whatever T.f
's return type restriction is. Thus it needs to be the case that Vector(T)
< Enumerable(T)
.
Mh, this works fine in the playground: https://play.crystal-lang.org/#/r/9zap
Currently, all instantiated containers must be referred to using their mangled names. This PR provides two ways to name those wrapper types:
Container(T)
: A module which contains no code. It is included by and only by the actual wrapper type with the same type arguments. It can be used as type restrictions.Container.of(T)
: A macro expression that produces the concrete, non-generic wrapper type. It can be used to construct containers in that wrapper type.In Qt5.cr only
QList
andQVector
have been wrapped so far. The.of
macro for the latter looks like this:For the special case of sequential containers, these wrappers also gain a
.from
constructor, which does exactly the same thing asBindgenHelper.wrap_container
, except again no mangled names are used (see below for an example). The.of
mechanism is independent of sequential containers.There are at least two issues that need to be addressed in future PRs:
QRgb
is an alias ofunsigned int
, thenQVector<QRgb>
is also an alias ofQVector<unsigned int>
. So far the container processors do not consider these aliases, so they end up instantiating both vector types. (qreal
should be turned into an alias as well.) Thus, alias detection should be extended to template arguments.Nested containers like
QVector<QVector<int>>
can only be obtained withQVector.of(Enumerable(Int32))
, not withQVector.of(QVector.of(Int32))
, due to the special passing rules of sequential containers. This expression becomes ambiguous ifQVector<QList<int>>
is also instantiated. Now that container types can be named by the user and that the.from
constructor makesEnumerable
conversion explicit, I suggest removing those passing rules after this PR, so Crystal arrays must be converted at the call site: (this removal would be a breaking change, so it needs to be accompanied by a version bump)