Overriding PinAttributes

Hey devvvvs,

Would it be possible to change InputAttribute and OutputAttribute behaviour to allow inheritance and overriding?

While developing the node suite for Message, I ran into some issues for inheritance between nodes. I used that to abstract out some kind of shared functionality, which usually includes pins as well.

This would be working quite nicely, if it weren’t for the Pin Attributes. They can only be defined in exactly one class: either the base class, or in all inheriting node class. This is bad, because I need to do final decisions in the base class, without any way to override this decision.

Let’s say, I want a functionality, where I decide the Formular for a node. In most cases, the users should only select a single Formular, so it makes sense to default that to IsSingle=true (so users can see it only uses 1).
However, there is this one odd node, where it makes sense to have a way to spread this. Currently, there is no way to have both the singular input in the base node (as default for inheriting nodes) and a spreaded input (for my odd node), unless I don’t use inheritance at all, and instead copy and paste shitloads of code.

have you tried introducing the pin that you want to spread with the new keyword? if you do so you can hide/overwrite the pin it would otherwise inherit from and that should also allow a new attribute.

yes, that’d be the expected behaviour, but it will not work. So this should probably be marked a bug.

Try this, and you will see.
The new keyword will introduce a second pin, instead of overriding it.


    using System;
    using VVVV.PluginInterfaces.V2;

    namespace VVVV.Nodes
    {
	#region PluginInfo
	[PluginInfo(Name = "PinTest", Category = "Value", Help = "Basic template with one value in/out", Tags = "")]
	#endregion PluginInfo
	public class ValuePinNode : IPluginEvaluate
	{
		[Input("Input", DefaultValue = 1.0, IsSingle = true)]
		public ISpread<double> FInput;

		[Output("Output")]
		public ISpread<double> FOutput;

		public void Evaluate(int SpreadMax)
		{
			FOutput.SliceCount = SpreadMax;
			for (int i = 0; i < SpreadMax; i++)
				FOutput[i] = FInput[i] * 2;
		}
	}

	#region PluginInfo
	[PluginInfo(Name = "PinOverride", Category = "Value", Help = "Basic template with one value in/out", Tags = "")]
	#endregion PluginInfo
	public class ValuePinOverrideNode : ValuePinNode 
	{
		[Input("Input", DefaultValue = 2.0, IsSingle = false)]
		public new ISpread<double> FInput;
	}
    }

also had this problem in the vvvv.audio pack. since i did not see an easy way to solve it, i decided to make a virtual method to create the pins manually and overwriting that in the sub class if needed.

yes, there are some cases where I resorted to that (e.g. in the zeromq pack), but

a) it is bloated
b) it feels dirty, and hard to read
c) it requires plugininterface v1 knowledge

would love to do it it properly with solid V2 fields

you can try to use the [ImportingConstructor] and in the sub class also have the pin attribute in the constructor and call the base constructor with the imported value. example:

also with plugin interface V2 there are more convenient methods where you create the attribute by hand and ask the IIOFactory to create a pin:

hey tonfilm,
Yes, elias showed me that trick. I’ve abused it extensively in zeromq simply to have different defaults in a config pin. That was another case, where I wished for simply overriding the DefaultBoolean flag instead of jumping these hoops.

With most c# frameworks this is no issue at all, overriding Attributes is something people are used to nowadays.
But now I am again at a crossroad because of the lack of this in the sdk:

I wish to introduce solid dx11 capabilities into Message (so dx11 types can fit into a Message), one which involves code for almost all of the 40+ nodes in the pack.

My life could be easy, I make all of them inherit a new Base class that handles transparently the dx11 registrations on its Message input pin during OnImportsSatisfied and open up an output pin. In the few cases where I only need either the input or the output, I simply hide the other via overriding Attributes, so it is not accessible from vvvv.
Or it could be hard, adding the registration code in every single node, polluting large portions of my code with dx11.
(That said, I’m sure I find a more elegant way about this in any case.)

Oh, I’ve hit this wall 4 times now, just in case you’re counting how often this is an actual issue ;)

since you said it’s only for one in or out pin, you could easily do it like this:
delay the pin creation, provide a method to alter the attribute, override that one if required in inherited, create pin in base


public class ValuePinNode : IPluginEvaluate, IPartImportsSatisfiedNotification
{
public ISpread< double > FInput;

[Import()]
public IIOFactory FFactory;

public void OnImportsSatisfied()
{
	var ioattr = new InputAttribute("Input"){IsSingle = true, DefaultValue = 1.0};
	ioattr = ChangeInput(ioattr);
	FInput = (ISpread<double>)FFactory.CreateIO(typeof(ISpread<double>), ioattr);
}

public virtual InputAttribute ChangeInput(InputAttribute ioattr)
{
	return ioattr;
}

public void Evaluate(int SpreadMax) { }

}

public class ValuePinOverrideNode : ValuePinNode
{
public override InputAttribute ChangeInput(InputAttribute ioAttr)
{
ioAttr.DefaultValue = 2;
return ioAttr;
}
}

1 Like

just tested the following: it is possible to declare the pin as virtual property in a base class, or as abstract property in an abstract base class:

                public abstract/virtual ISpread<double> FInput
		{
			get;
			set;
		}

then override in the subclass:

		[Input("Input 2", DefaultValue = 2.0)]
		public override ISpread<double> FInput
		{
			get;
			set;
		}

does this help?

Yes, as a work-around this is another option, just like the suggestions above. But it does not answer (nor actually solve) the question from the thead start:

Would it be possible to change InputAttribute and OutputAttribute behaviour to allow inheritance and overriding?

For that, we would be able to annotate the abstract/virtual Field as well, and override it with the second one. Quite straight forward really.

Maybe I shouldn’t have elaborated on my “special” cases as much, and stressed the general importance of a properly working inheritance in nodes. I thought that explaining some situations where this bug was a bummer might help my case of getting the sdk fixed.

It’s been remarked that more people in the business of developing packs have run into that problem, and were forced to write code to create the pins in question at node runtime, instead of just marking a field with the appropriate Attributes.

So I would appreciate it, if we could go back a step again, to the original question: Can this be fixed? The reluctance to actually discuss the question starts to worry me, that similar work-arounds will be necessary with VL.

Ok this was much harder to implement than expected, basically because MEF (the thing used for dependency injection) is not that straight forward as one might think.
Anyways checkout https://github.com/vvvv/vvvv-sdk/commit/f34b8213beb0032b6c96ba3074dc8a141f2ec616 and following code works:

#region usings
using System;
using System.ComponentModel.Composition;

using VVVV.PluginInterfaces.V1;
using VVVV.PluginInterfaces.V2;
using VVVV.Utils.VColor;
using VVVV.Utils.VMath;

using VVVV.Core.Logging;
#endregion usings

namespace VVVV.Nodes
{
    [PluginInfo(Name = "Base", Category = "Value")]
    public class SomeBase : IPluginEvaluate
    {
        [Input("Input", DefaultValue = 1.0)]
        public virtual ISpread<double> FInput { get; set; }

        public virtual void Evaluate(int SpreadMax)
        {
        }
    }

    [PluginInfo(Name = "Foo", Category = "Value")]
    public class Foo : SomeBase
    {
        [Input("Input Foo", DefaultValue = 5.0)]
        public override ISpread<double> FInput { get; set; }
    }

    [PluginInfo(Name = "Bar", Category = "Value")]
    public class Bar : SomeBase
    {
        [Input("Input Bar", DefaultValue = 50.0)]
        public override ISpread<double> FInput { get; set; }
    }

    [PluginInfo(Name = "Lala", Category = "Value")]
    public class Lala : SomeBase
    {

    }
}

Happy hacking!

4 Likes

Ah, thanks Elias! I am sure I will not be the only one that’s glad not to jump hoops anymore.

And also thanks to all the other people trying to provide workarounds. Did not mean to downplay your responses, but I wasn’t after a personal solution here, but for one that is easy and intuitive for all devs.
This thread turned into quite a knowledgebase about dynamically created pins though, might have to bookmark it ;)

Today I was able to test it. Want to report my findings.

So it does work, as you showed, when all pins are defined as Fields Properties (with the get and set stuff), and when the inheriting classes use the override keyword on that Fields Property.

It does not work, when using the new keyword, hence it does not work on plain members (like most pins are actually declared).

So far, so good. This helps already heaps do what I need to do. But for future reference, having this working for properties would rock even more.

i think you are mixing the terms property and field up.
properties can have virtual or abstract get/set methods that can be overwritten in a sub class. properties have an auto generated backing field.
fields are data members of a type and can not be overwritten, the new keyword creates a new data member that is not related to the one in the parent class, so it makes all sense what you are describing. but it will not be possible to overwrite attributes for fields, since they cannot be virtual or abstract.

yes that is right. i sure meant member variables, without any getter/setter.

anyway, with a new keyword, either one should hide the inherited member, which should reflect on the attribute as well. right now, that’s being ignored, only the base class is being considered.

sounds practical in that scenario, but if you use the new keyword it is technically the same as defining a new data member with a different name. so the one of the parent class does also co-exist and will be used in the parent class. i think i will only lead to very hard to find bugs if it would behave like it was inherited…

I think, now you mix something up. The new keyword actually hides the inherited member with that name, making it unaccessible (unless used explicitly in the scope of the base class).

So in the case you use new in a certain plugin, it would be like you’ve never defined anything in the base class. Which actually creates no confusion at all, if you think about it, because you are completely redefining it.

The only confusion would occur, if the fields don’t match, or if ppl start working with the Attributes themselves (which is both extremely unlikely).

Well it is like tebjan said. New introduces a new field and there’s no connection between them in IL/reflection. So I don’t really see a way to implement what you’re asking for. But virtual properties should do just fine

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.