On Thursday, 6 September 2018 at 20:25:18 UTC, Neia Neutuladh wrote:
On Thursday, 6 September 2018 at 10:18:43 UTC, Josphe Brigmo wrote:

Variants can hold an arbitrary set of types.

I imagine that it is effectively just a type id and an object pointer!?

It's a typeid and a static array large enough to hold any basic builtin type: the now-deprecated creal, a dynamic array, or a delegate.

If you make a Variant from an object, it stores that object reference. The object reference is just a pointer, yes.

If you make a Variant from a 256-byte struct, it copies that struct onto the heap and stores a pointer.

If you make a Variant from a 6-byte struct, then it stores that struct and does no heap allocations.

If so, then it really is just a special type of a class class.

It's similar to a java.lang.Object with explicit boxing, but without the need to create a new wrapper class for each value type.

It seems that variant and oop are essentially the same thing, more or less, as whatever can be done in one can effectively be done in the other, except, of course, that the class version has compile time type information associated with it, which sort of restricts variant to a subset of all types!?!

Object-oriented programming includes inheritance and member function overloading. Variant doesn't; it's just about storage.

We are talking about two different things that are related:

A variant holds a set of objects. Using VariantClass limits the types to a subset and allows for inherited types to be added.

Those objects may be classes which already have inheritance and hence matching and calling their methods will dispatch appropriately.

A variant sits on top of the object hierarchy, it is not somewhere in the middle where objects will inherit from it(which is impossible).

The difference is simply

Object x;
Variant y;

There is very little difference. If x is a class type then so will variant hold class type and it will act just like the Object does.

That is, Variant can do no worse than just being an object(except it then becomes pointless as it can hold only one type.


If you're working with classes, you'd be better off using a base class or interface instead of Variant for fields that can only hold objects of those types.

But variant can reduce the code complexity if one restricts it's inputs to a specific class of types:

Yes, for which you can use std.variant.Algebraic. For instance, Algebraic!(int, long, float) will accept ints, longs, and floats, but nothing else.

It is not the same since Algebraic does not allow inherited types inside it's container? Or maybe it does?

VariantClass!X will accept anything derived from X and it will dispatch appropriately since it just delegates to the normal class dispatching mechanisms.



Then VariantClass will prevent any arbitrary type from being assigned to the variant, effectively allow inheritance to be used(in the sense that it will prevent any type from being used at compile time, like inheritance):

VariantClass!X v; // only T : X's are allowed.

That's equivalent to `X v;` except with a wrapper around it.


Yes, but the whole point of the wrapper is simply to insure that only a subset of types is used but allow for different types.

X v;

only allows types derived from X.

VariantClass!(X, Y) v;

allows types derived from X or from Y.

then matching on the type will simply delegate everything appropriately.

If X and Y have a common type I then one can do

VariantClass!I v;

which would be the same as

I v;

But not the same as

Algebraic!I v;

because we couldn't stick in a derived object for I. We can cast, and it works but it simply doesn't naturally allow derived types for some reason.

VariantClass allows derived types. This is a big difference because Algebraic doesn't naturally work well with oop but VariantClass does.





Matching then is dispatch. We could further extend VariantClass to return specific classes for each type that dispatch to match and vice versa.

I think you're saying that this sort of Algebraic could expose any methods and fields common to all its types?

Can D project interfaces like this?

interface I
{
   int foo(int);
}

I i = project!I(o);

You can write code to make that work. It would create a wrapper class that implements the requested interface, and that wrapper would just forward everything to the wrapped value.

It would be a lot easier just to have the type implement the interface itself, if that's possible.

But this is just oop.

Opp requires more work to design because one has to implement the interfaces in a prescribed way.

What I am talking about taking any object and if it has certain methods that conform to some "interface" then it will behave as if it were derived... even if it were not specified as derived.

Why this is better is because it allows objects that may not have been inherited from some interface in the design(pre-existing).

interface I { int foo(int); }
class A
{
  int foo(int);
}

DuckingVariant!I v;

A a;
v = a;

then

A, for all practical purposes is an I and v.foo can be called.

DuckingVariant simply checks if the type can be projected on to I, which it can in this case.

Now, if A has meta info but no way to change the source, then DuckingVariant could simply check if it matches the specified interfaces(s) and if it does, do the mapping(which is just forwarding).


Of course, if A changes the name of foo the everything will break and since A doesn't implement I it is more prone to happen... Also, such meta info isn't generally included in the binaries so there is really no way to use DuckingVariant in general with arbitrary code.

Remember, the idea is simply to wrap an object that has some simple functionality needed such as implementing a few functions. Seems using oop is overkill when it is not needed. Compile time DbI would work fine but it doesn't work for runtime. So DuckingVariant would sort of bridge the two by making DbI work at run time, assuming it could peak at the meta info of the type.

If one starts getting to fancy then basically one is just implementing COM with queryinterface and such.

Seems though this cannot be used at runtime though since function signatures are not transported in the binary? If they are, then maybe it would work and would reduce the overhead of oop as one could just project types to other types that overlap.

You can use the witchcraft library on dub to perform runtime introspection, but there would be a performance penalty.


Cool, I will check it out. It seems it would allow DuckingVariant to be created.

Reply via email to