2021-03-15 14:00:26 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
2022-08-19 12:14:31 -04:00
# include "Interfaces/MetasoundFrontendSourceInterface.h"
2021-03-15 14:00:26 -04:00
# include "Internationalization/Text.h"
2021-09-13 14:14:37 -04:00
# include "MetasoundArrayNodes.h"
2021-03-15 14:00:26 -04:00
# include "MetasoundBuilderInterface.h"
# include "MetasoundDataFactory.h"
# include "MetasoundExecutableOperator.h"
# include "MetasoundFacade.h"
# include "MetasoundLog.h"
# include "MetasoundNodeInterface.h"
# include "MetasoundOperatorInterface.h"
2022-03-17 13:14:50 -04:00
# include "MetasoundParamHelper.h"
2021-03-15 14:00:26 -04:00
# include "MetasoundPrimitives.h"
# include "MetasoundTrigger.h"
2021-09-13 14:14:37 -04:00
# include "MetasoundVertex.h"
2021-04-05 20:22:19 -04:00
# include "Misc/ScopeLock.h"
2021-03-15 14:00:26 -04:00
# include <type_traits>
# define LOCTEXT_NAMESPACE "MetasoundFrontend"
namespace Metasound
{
/** Shuffle Node Vertex Names */
namespace ArrayNodeShuffleVertexNames
{
2022-03-17 13:14:50 -04:00
METASOUND_PARAM ( InputTriggerNext , " Next " , " Trigger to get the next value in the shuffled array. " )
METASOUND_PARAM ( InputTriggerShuffle , " Shuffle " , " Trigger to shuffle the array manually. " )
METASOUND_PARAM ( InputTriggerReset , " Reset Seed " , " Trigger to reset the random seed stream of the shuffle node. " )
METASOUND_PARAM ( InputShuffleArray , " In Array " , " Input Array. " )
METASOUND_PARAM ( InputShuffleSeed , " Seed " , " Seed to use for the the random shuffle. " )
METASOUND_PARAM ( InputAutoShuffle , " Auto Shuffle " , " Set to true to automatically shuffle when the array has been read. " )
METASOUND_PARAM ( InputShuffleEnableSharedState , " Enable Shared State " , " Set to enabled shared state across instances of this metasound. " )
2021-03-15 14:00:26 -04:00
2022-03-17 13:14:50 -04:00
METASOUND_PARAM ( OutputTriggerOnNext , " On Next " , " Triggers when the \" Next \" input is triggered. " )
METASOUND_PARAM ( OutputTriggerOnShuffle , " On Shuffle " , " Triggers when the \" Shuffle \" input is triggered or if the array is auto-shuffled. " )
METASOUND_PARAM ( OutputTriggerOnResetSeed , " On Reset Seed " , " Triggers when the \" Reset Seed \" input is triggered. " )
METASOUND_PARAM ( OutputShuffledValue , " Value " , " Value of the current shuffled element. " )
2021-03-15 14:00:26 -04:00
}
class METASOUNDFRONTEND_API FArrayIndexShuffler
{
public :
FArrayIndexShuffler ( ) = default ;
FArrayIndexShuffler ( int32 InSeed , int32 MaxIndices ) ;
void Init ( int32 InSeed , int32 MaxIndices ) ;
void SetSeed ( int32 InSeed ) ;
void ResetSeed ( ) ;
// Returns the next value in the array indices. Returns true if the array was re-shuffled automatically.
bool NextValue ( bool bAutoShuffle , int32 & OutIndex ) ;
// Shuffle the array with the given max indices
void ShuffleArray ( ) ;
private :
// Helper function to swap the current index with a random index
void RandomSwap ( int32 InCurrentIndex , int32 InStartIndex , int32 InEndIndex ) ;
// The current index into the array of indicies (wraps between 0 and ShuffleIndices.Num())
int32 CurrentIndex = 0 ;
// The previously returned value. Used to avoid repeating the last value on shuffle.
int32 PrevValue = INDEX_NONE ;
// Array of indices (in order 0 to Num), shuffled
TArray < int32 > ShuffleIndices ;
// Random stream to use to randomize the shuffling
FRandomStream RandomStream ;
} ;
class FSharedStateShuffleManager
{
public :
static FSharedStateShuffleManager & Get ( )
{
static FSharedStateShuffleManager GSM ;
return GSM ;
}
void InitSharedState ( uint32 InSharedStateId , int32 InSeed , int32 InNumElements )
{
FScopeLock Lock ( & CritSect ) ;
if ( ! Shufflers . Contains ( InSharedStateId ) )
{
Shufflers . Add ( InSharedStateId , MakeUnique < FArrayIndexShuffler > ( InSeed , InNumElements ) ) ;
}
}
bool NextValue ( uint32 InSharedStateId , bool bAutoShuffle , int32 & OutIndex )
{
FScopeLock Lock ( & CritSect ) ;
TUniquePtr < FArrayIndexShuffler > * Shuffler = Shufflers . Find ( InSharedStateId ) ;
return ( * Shuffler ) - > NextValue ( bAutoShuffle , OutIndex ) ;
}
void SetSeed ( uint32 InSharedStateId , int32 InSeed )
{
FScopeLock Lock ( & CritSect ) ;
TUniquePtr < FArrayIndexShuffler > * Shuffler = Shufflers . Find ( InSharedStateId ) ;
( * Shuffler ) - > SetSeed ( InSeed ) ;
}
void ResetSeed ( uint32 InSharedStateId )
{
FScopeLock Lock ( & CritSect ) ;
TUniquePtr < FArrayIndexShuffler > * Shuffler = Shufflers . Find ( InSharedStateId ) ;
( * Shuffler ) - > ResetSeed ( ) ;
}
void ShuffleArray ( uint32 InSharedStateId )
{
FScopeLock Lock ( & CritSect ) ;
TUniquePtr < FArrayIndexShuffler > * Shuffler = Shufflers . Find ( InSharedStateId ) ;
( * Shuffler ) - > ShuffleArray ( ) ;
}
private :
FSharedStateShuffleManager ( ) = default ;
~ FSharedStateShuffleManager ( ) = default ;
FCriticalSection CritSect ;
TMap < uint32 , TUniquePtr < FArrayIndexShuffler > > Shufflers ;
} ;
/** TArrayShuffleOperator shuffles an array on trigger and outputs values sequentially on "next". It avoids repeating shuffled elements and supports auto-shuffling.*/
template < typename ArrayType >
class TArrayShuffleOperator : public TExecutableOperator < TArrayShuffleOperator < ArrayType > >
{
public :
using FArrayDataReadReference = TDataReadReference < ArrayType > ;
using ElementType = typename MetasoundArrayNodesPrivate : : TArrayElementType < ArrayType > : : Type ;
using FElementTypeWriteReference = TDataWriteReference < ElementType > ;
static const FVertexInterface & GetDefaultInterface ( )
{
using namespace ArrayNodeShuffleVertexNames ;
static const FVertexInterface DefaultInterface (
FInputVertexInterface (
2022-03-31 16:49:59 -04:00
TInputDataVertex < FTrigger > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputTriggerNext ) ) ,
TInputDataVertex < FTrigger > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputTriggerShuffle ) ) ,
TInputDataVertex < FTrigger > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputTriggerReset ) ) ,
TInputDataVertex < ArrayType > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputShuffleArray ) ) ,
TInputDataVertex < int32 > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputShuffleSeed ) , - 1 ) ,
TInputDataVertex < bool > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputAutoShuffle ) , true ) ,
TInputDataVertex < bool > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( InputShuffleEnableSharedState ) , false )
2021-03-15 14:00:26 -04:00
) ,
FOutputVertexInterface (
2022-03-31 16:49:59 -04:00
TOutputDataVertex < FTrigger > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( OutputTriggerOnNext ) ) ,
TOutputDataVertex < FTrigger > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( OutputTriggerOnShuffle ) ) ,
TOutputDataVertex < FTrigger > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( OutputTriggerOnResetSeed ) ) ,
TOutputDataVertex < ElementType > ( METASOUND_GET_PARAM_NAME_AND_METADATA ( OutputShuffledValue ) )
2021-03-15 14:00:26 -04:00
)
) ;
return DefaultInterface ;
}
static const FNodeClassMetadata & GetNodeInfo ( )
{
auto CreateNodeClassMetadata = [ ] ( ) - > FNodeClassMetadata
{
2021-05-10 16:56:43 -04:00
const FName DataTypeName = GetMetasoundDataTypeName < ArrayType > ( ) ;
const FName OperatorName = " Shuffle " ;
2022-02-10 18:36:47 -05:00
const FText NodeDisplayName = METASOUND_LOCTEXT_FORMAT ( " ArrayOpArrayShuffleDisplayNamePattern " , " Shuffle ({0}) " , GetMetasoundDataTypeDisplayText < ArrayType > ( ) ) ;
const FText NodeDescription = METASOUND_LOCTEXT ( " ArrayOpArrayShuffleDescription " , " Output next element of a shuffled array on trigger. " ) ;
2021-05-10 16:56:43 -04:00
const FVertexInterface NodeInterface = GetDefaultInterface ( ) ;
2021-03-15 14:00:26 -04:00
return MetasoundArrayNodesPrivate : : CreateArrayNodeClassMetadata ( DataTypeName , OperatorName , NodeDisplayName , NodeDescription , NodeInterface ) ;
} ;
static const FNodeClassMetadata Metadata = CreateNodeClassMetadata ( ) ;
return Metadata ;
}
2023-10-13 19:29:51 -04:00
static TUniquePtr < IOperator > CreateOperator ( const FBuildOperatorParams & InParams , FBuildResults & OutResults )
2021-03-15 14:00:26 -04:00
{
using namespace ArrayNodeShuffleVertexNames ;
using namespace MetasoundArrayNodesPrivate ;
2023-10-13 19:29:51 -04:00
const FInputVertexInterfaceData & InputData = InParams . InputData ;
2021-03-15 14:00:26 -04:00
2023-10-13 19:29:51 -04:00
TDataReadReference < FTrigger > InTriggerNext = InputData . GetOrCreateDefaultDataReadReference < FTrigger > ( METASOUND_GET_PARAM_NAME ( InputTriggerNext ) , InParams . OperatorSettings ) ;
TDataReadReference < FTrigger > InTriggerShuffle = InputData . GetOrCreateDefaultDataReadReference < FTrigger > ( METASOUND_GET_PARAM_NAME ( InputTriggerShuffle ) , InParams . OperatorSettings ) ;
TDataReadReference < FTrigger > InTriggerReset = InputData . GetOrCreateDefaultDataReadReference < FTrigger > ( METASOUND_GET_PARAM_NAME ( InputTriggerReset ) , InParams . OperatorSettings ) ;
FArrayDataReadReference InInputArray = InputData . GetOrCreateDefaultDataReadReference < ArrayType > ( METASOUND_GET_PARAM_NAME ( InputShuffleArray ) , InParams . OperatorSettings ) ;
TDataReadReference < int32 > InSeedValue = InputData . GetOrCreateDefaultDataReadReference < int32 > ( METASOUND_GET_PARAM_NAME ( InputShuffleSeed ) , InParams . OperatorSettings ) ;
TDataReadReference < bool > bInAutoShuffle = InputData . GetOrCreateDefaultDataReadReference < bool > ( METASOUND_GET_PARAM_NAME ( InputAutoShuffle ) , InParams . OperatorSettings ) ;
TDataReadReference < bool > bInEnableSharedState = InputData . GetOrCreateDefaultDataReadReference < bool > ( METASOUND_GET_PARAM_NAME ( InputShuffleEnableSharedState ) , InParams . OperatorSettings ) ;
2021-03-15 14:00:26 -04:00
return MakeUnique < TArrayShuffleOperator > ( InParams , InTriggerNext , InTriggerShuffle , InTriggerReset , InInputArray , InSeedValue , bInAutoShuffle , bInEnableSharedState ) ;
}
TArrayShuffleOperator (
2023-10-13 19:29:51 -04:00
const FBuildOperatorParams & InParams ,
2021-03-15 14:00:26 -04:00
const TDataReadReference < FTrigger > & InTriggerNext ,
const TDataReadReference < FTrigger > & InTriggerShuffle ,
const TDataReadReference < FTrigger > & InTriggerReset ,
const FArrayDataReadReference & InInputArray ,
const TDataReadReference < int32 > & InSeedValue ,
const TDataReadReference < bool > & bInAutoShuffle ,
const TDataReadReference < bool > & bInEnableSharedState )
: TriggerNext ( InTriggerNext )
, TriggerShuffle ( InTriggerShuffle )
, TriggerReset ( InTriggerReset )
, InputArray ( InInputArray )
, SeedValue ( InSeedValue )
, bAutoShuffle ( bInAutoShuffle )
, bEnableSharedState ( bInEnableSharedState )
, TriggerOnShuffle ( FTriggerWriteRef : : CreateNew ( InParams . OperatorSettings ) )
, OutValue ( TDataWriteReferenceFactory < ElementType > : : CreateAny ( InParams . OperatorSettings ) )
{
2023-02-22 17:54:26 -05:00
Reset ( InParams ) ;
2021-03-15 14:00:26 -04:00
}
virtual ~ TArrayShuffleOperator ( ) = default ;
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
virtual void BindInputs ( FInputVertexInterfaceData & InOutVertexData ) override
2021-03-15 14:00:26 -04:00
{
using namespace ArrayNodeShuffleVertexNames ;
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputTriggerNext ) , TriggerNext ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputTriggerShuffle ) , TriggerShuffle ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputTriggerReset ) , TriggerReset ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputShuffleArray ) , InputArray ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputShuffleSeed ) , SeedValue ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputAutoShuffle ) , bAutoShuffle ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( InputShuffleEnableSharedState ) , bEnableSharedState ) ;
}
2021-03-15 14:00:26 -04:00
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
virtual void BindOutputs ( FOutputVertexInterfaceData & InOutVertexData ) override
{
using namespace ArrayNodeShuffleVertexNames ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( OutputTriggerOnNext ) , TriggerNext ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( OutputTriggerOnShuffle ) , TriggerOnShuffle ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( OutputTriggerOnResetSeed ) , TriggerReset ) ;
InOutVertexData . BindReadVertex ( METASOUND_GET_PARAM_NAME ( OutputShuffledValue ) , OutValue ) ;
}
virtual FDataReferenceCollection GetInputs ( ) const override
{
// This should never be called. Bind(...) is called instead. This method
// exists as a stop-gap until the API can be deprecated and removed.
checkNoEntry ( ) ;
return { } ;
2021-03-15 14:00:26 -04:00
}
virtual FDataReferenceCollection GetOutputs ( ) const override
{
[Metasound Bind] Fixup for bind issues caught by new automated tests.
#jira UE-187406, UE-187390, UE-187404, UE-187403, UE-187405, UE-187392, UE-187391, UE-187395, UE-187399, UE-187398, UE-187389, UE-187393, UE-187394, UE-187396, UE-187397, UE-187401
#rb phil.popp
[CL 26130674 by maxwell hayes in ue5-main branch]
2023-06-20 15:08:54 -04:00
// This should never be called. Bind(...) is called instead. This method
// exists as a stop-gap until the API can be deprecated and removed.
checkNoEntry ( ) ;
return { } ;
2021-03-15 14:00:26 -04:00
}
2023-02-22 17:54:26 -05:00
void Reset ( const IOperator : : FResetParams & InParams )
{
using namespace Frontend ;
SharedStateUniqueId = INDEX_NONE ;
if ( InParams . Environment . Contains < uint32 > ( SourceInterface : : Environment : : SoundUniqueID ) )
{
// Get the environment variable for the unique ID of the sound
SharedStateUniqueId = InParams . Environment . GetValue < uint32 > ( SourceInterface : : Environment : : SoundUniqueID ) ;
}
PrevSeedValue = * SeedValue ;
InitializeShufflers ( InputArray - > Num ( ) ) ;
TriggerOnShuffle - > Reset ( ) ;
* OutValue = TDataTypeFactory < ElementType > : : CreateAny ( InParams . OperatorSettings ) ;
}
2021-03-15 14:00:26 -04:00
void Execute ( )
{
TriggerOnShuffle - > AdvanceBlock ( ) ;
const ArrayType & InputArrayRef = * InputArray ;
// Don't do anything if our array is empty
if ( InputArrayRef . Num ( ) = = 0 )
{
return ;
}
2023-01-25 20:41:44 -05:00
if ( PrevArraySize ! = InputArrayRef . Num ( ) )
{
2023-02-22 17:54:26 -05:00
InitializeShufflers ( InputArrayRef . Num ( ) ) ;
2023-01-25 20:41:44 -05:00
}
// Check for a seed change
if ( PrevSeedValue ! = * SeedValue )
{
Seed ( * SeedValue ) ;
}
2021-03-15 14:00:26 -04:00
TriggerReset - > ExecuteBlock (
[ & ] ( int32 StartFrame , int32 EndFrame )
{
} ,
[ this ] ( int32 StartFrame , int32 EndFrame )
{
2023-01-25 20:41:44 -05:00
Reset ( ) ;
2021-03-15 14:00:26 -04:00
}
) ;
TriggerShuffle - > ExecuteBlock (
[ & ] ( int32 StartFrame , int32 EndFrame )
{
} ,
[ this ] ( int32 StartFrame , int32 EndFrame )
{
2023-01-25 20:41:44 -05:00
Shuffle ( ) ;
2021-03-15 14:00:26 -04:00
TriggerOnShuffle - > TriggerFrame ( StartFrame ) ;
}
) ;
TriggerNext - > ExecuteBlock (
[ & ] ( int32 StartFrame , int32 EndFrame )
{
} ,
[ this ] ( int32 StartFrame , int32 EndFrame )
{
const ArrayType & InputArrayRef = * InputArray ;
int32 OutShuffleIndex = INDEX_NONE ;
2023-01-25 20:41:44 -05:00
bool bShuffleTriggered = GetNextIndex ( OutShuffleIndex ) ;
2021-03-15 14:00:26 -04:00
// The input array size may have changed, so make sure it's wrapped into range of the input array
* OutValue = InputArrayRef [ OutShuffleIndex % InputArrayRef . Num ( ) ] ;
// Trigger out if the array was auto-shuffled
if ( bShuffleTriggered )
{
TriggerOnShuffle - > TriggerFrame ( StartFrame ) ;
}
}
) ;
}
private :
2023-02-22 17:54:26 -05:00
void InitializeShufflers ( int32 InSize )
2023-01-25 20:41:44 -05:00
{
PrevArraySize = InSize ;
if ( InSize > 0 )
{
if ( SharedStateUniqueId ! = INDEX_NONE )
{
FSharedStateShuffleManager & SM = FSharedStateShuffleManager : : Get ( ) ;
SM . InitSharedState ( SharedStateUniqueId , PrevSeedValue , InSize ) ;
}
ArrayIndexShuffler = MakeUnique < FArrayIndexShuffler > ( PrevSeedValue , InSize ) ;
}
}
void Seed ( int32 InNewSeed )
{
PrevSeedValue = InNewSeed ;
if ( SharedStateUniqueId ! = INDEX_NONE )
{
FSharedStateShuffleManager & SM = FSharedStateShuffleManager : : Get ( ) ;
SM . SetSeed ( SharedStateUniqueId , InNewSeed ) ;
}
if ( ArrayIndexShuffler . IsValid ( ) )
{
ArrayIndexShuffler - > SetSeed ( InNewSeed ) ;
}
}
void Reset ( )
{
if ( SharedStateUniqueId ! = INDEX_NONE )
{
FSharedStateShuffleManager & SM = FSharedStateShuffleManager : : Get ( ) ;
SM . ResetSeed ( SharedStateUniqueId ) ;
}
if ( ArrayIndexShuffler . IsValid ( ) )
{
ArrayIndexShuffler - > ResetSeed ( ) ;
}
}
bool GetNextIndex ( int32 & OutIndex )
{
if ( * bEnableSharedState & & SharedStateUniqueId ! = INDEX_NONE )
{
FSharedStateShuffleManager & SM = FSharedStateShuffleManager : : Get ( ) ;
return SM . NextValue ( SharedStateUniqueId , * bAutoShuffle , OutIndex ) ;
}
else if ( ArrayIndexShuffler . IsValid ( ) )
{
return ArrayIndexShuffler - > NextValue ( * bAutoShuffle , OutIndex ) ;
}
else
{
checkNoEntry ( ) ;
OutIndex = 0 ;
return false ;
}
}
void Shuffle ( )
{
if ( SharedStateUniqueId ! = INDEX_NONE )
{
FSharedStateShuffleManager & SM = FSharedStateShuffleManager : : Get ( ) ;
SM . ShuffleArray ( SharedStateUniqueId ) ;
}
if ( ArrayIndexShuffler . IsValid ( ) )
{
ArrayIndexShuffler - > ShuffleArray ( ) ;
}
}
2021-03-15 14:00:26 -04:00
// Inputs
TDataReadReference < FTrigger > TriggerNext ;
TDataReadReference < FTrigger > TriggerShuffle ;
TDataReadReference < FTrigger > TriggerReset ;
FArrayDataReadReference InputArray ;
TDataReadReference < int32 > SeedValue ;
TDataReadReference < bool > bAutoShuffle ;
TDataReadReference < bool > bEnableSharedState ;
// Outputs
TDataWriteReference < FTrigger > TriggerOnShuffle ;
TDataWriteReference < ElementType > OutValue ;
// Data
TUniquePtr < FArrayIndexShuffler > ArrayIndexShuffler ;
int32 PrevSeedValue = INDEX_NONE ;
2023-01-25 20:41:44 -05:00
int32 PrevArraySize = 0 ;
2021-03-15 14:00:26 -04:00
uint32 SharedStateUniqueId = INDEX_NONE ;
} ;
template < typename ArrayType >
class TArrayShuffleNode : public FNodeFacade
{
public :
TArrayShuffleNode ( const FNodeInitData & InInitData )
: FNodeFacade ( InInitData . InstanceName , InInitData . InstanceID , TFacadeOperatorClass < TArrayShuffleOperator < ArrayType > > ( ) )
{
}
virtual ~ TArrayShuffleNode ( ) = default ;
} ;
}
# undef LOCTEXT_NAMESPACE