この記事はvvvv Advent Calendar 2016の14日目の記事でしたが、風邪をひいてしまい締め切りに間にあいませんでした、、、
鼻水をすすりながらの投稿になります。
なぜプラグインを作るのか?
vvvvはそれだけでもプログラミング言語として優れていますが、苦手な処理もあります。例えば枚フレームごとに数値を加算していくような状態を持ったものがそれです。(FrameDelayを使えばできたりもしますが、パッチの見通しが悪くなってしまいがちです)
それを解決するための方法の一つとして、C#でカスタムノードを書いてそれを使うというものがありますが、今回紹介するのはそこの斜め上の、カスタムノードをC++で書こう!というものです。
C++でカスタムノードが書けるようになると、C#と同様なメリットの他に、C/C++で提供されている多種多様なライブラリやデバイスAPIを、vvvvのカスタムノードとして自由に使えるようになります!夢がふくらみますね!
C++でノードを作る
さてC++でカスタムノードを作る手順ですが、その工程のほとんどがVisual Studioの使い方みたいになってしまったのでスクリーンキャプチャをとってyoutubeに上げました。そちらをご覧ください
内容としては:
- プロジェクトを作成する
- 完成品のソースコードをもってきてdllができるのを確認する
- 任意の場所にdllを作成できるようにする
- テスト用のv4pをひっかけてVisual Studio上からBuild & Runできるようにする
という工程の説明になります
この動画の中で作ったプロジェクトはここに → https://cl.ly/17421G3l0R3O
動画中で使ったソースコードの解説
今回の例で使ったのは、C#のバリューノードのtemplateから生成されるコードをそのままC++に約したものです。
C++、とずっと書いてきましたが、より正確にはC++/CLIと呼ばれるMicrosoftのC++の拡張言語を使っています。
C++/CLIはひらたく言えば.NET Frameworkを使えるようにしたC++といった赴きのもののようで、C++/CLIの流儀に従って書いていけばC#やVIsual Basic等々、.NET Frameworkを使っている言語をまたいで相互呼び出しが可能になる仕組みのようです。
vvvvのC#のレイヤーからC++のコードが呼び出せるのも.NET Frameworkのおかげなのでした。
C++/CLIで拡張された文法や機能の部分もほぼC#との差分になっており、見た目にもわかりやすいものになっています。
↓ がC#のほうの元のソースコード
#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
{
#region PluginInfo
[PluginInfo(Name = "CSharpTest", Category = "CSharpTest", Tags = "")]
#endregion PluginInfo
public class CSharpTestNode : IPluginEvaluate
{
#region fields & pins
[Input("Input", DefaultValue = 1.0)]
public ISpread<double> FInput;
[Output("Output")]
public ISpread<double> FOutput;
[Import()]
public ILogger FLogger;
#endregion fields & pins
public void Evaluate(int SpreadMax)
{
FOutput.SliceCount = SpreadMax;
for (int i = 0; i < SpreadMax; i++)
FOutput[i] = FInput[i] * 2;
FLogger.Log(LogType.Debug, "hi tty!");
}
}
}
一方、↓ がC++/CLIに書き直した実装です
// TestNativePlugin.h
#pragma once
using namespace System;
using namespace System::ComponentModel::Composition;
using namespace VVVV::PluginInterfaces::V1;
using namespace VVVV::PluginInterfaces::V2;
using namespace VVVV::Utils::VColor;
using namespace VVVV::Utils::VMath;
using namespace VVVV::Core::Logging;
using namespace System;
namespace VVVV
{
namespace Nodes
{
[PluginInfo(Name = "CppCliTest", Category = "CppCliTest", Tags = "")]
public ref class CppCliTestNode : public VVVV::PluginInterfaces::V2::IPluginEvaluate
{
public:
[Input("Input", DefaultValue = 1.0)]
ISpread<double>^ FInput;
[Output("Output")]
ISpread<double>^ FOutput;
[Import()]
ILogger^ FLogger;
void Evaluate(int SpreadMax) override;
};
}
}
// TestNativePlugin.cpp
#include "stdafx.h"
#include "TestNativePlugin.h"
void VVVV::Nodes::CppCliTestNode::Evaluate(int SpreadMax)
{
FOutput->SliceCount = SpreadMax;
for (int i = 0; i < SpreadMax; i++)
FOutput[i] = FInput[i] * 2;
FLogger->Log(LogType::Debug, "hi tty!");
}
見た感じは大体いっしょですかね?
基本的にネームスペースの宣言の違いと、C++的にはあまり見慣れない ISpread<double>^
のような宣言が主な違いでしょうか。^
は、.NET Framework側のGC管理されたオブジェクトを保持するためのポインタのような参照のようなもの、という意味らしいです。
今回は文法や細かな部分までは深追いしないようにしますので、より詳しくはこちらなど… → https://ja.wikipedia.org/wiki/C%2B%2B/CLI
まとめ
C#にはマネージドと呼ばれるGCにメモリ管理をしてもらうモードがデフォルトとなっていて、プログラマ側が明示的に宣言をしないとアンマネージド(メモリの手動管理)のモードに移らないようになっているようです。
ずっとC++をやってきた身としては、メモリが自動でコントロールされるというのが結構気持ち悪い所だったので、色々調べた結果プラグインがC++で書けるようになって本当に良かった、、、という気持ちでいっぱいです。。。
とはいえ、これからずっとC#を避けていける気もしないのと、C# / .NET Frameworksのお手軽さも捨て難いので、これを機にきちんと勉強しないと、、、と思っております!いい参考書などありましたら教えてください!!!
今回はC++/CLIのベーシックな所の紹介で力つきてしまい夢が広がる所まで行けなかったのですが、、、、
これにて!