// MQL4&5-code #define __TYPETOBYTES__ template class CASTING { public: template static const T Casting( const T1 &Value ) { #ifdef TYPETOBYTES_FULL_SLOW static const bool IsScript = ((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE) == PROGRAM_SCRIPT); static bool FirstRun = true; T Data = {0}; if (FirstRun) { if (IsScript) { const int handle = ::FileOpen("Casting.tmp", FILE_READ | FILE_WRITE | FILE_BIN); if (handle != INVALID_HANDLE) { ::FileWriteStruct(handle, Value); ::FileSeek(handle, 0, SEEK_SET); ::FileReadStruct(handle, Data); ::FileClose(handle); } } else { ::Alert("TYPETOBYTES_FULL_SLOW-mode only for Scripts!"); FirstRun = false; } } return(Data); #else // TYPETOBYTES_FULL_SLOW union CAST { T1 Value1; const T Value2; CAST( const T1 &Value) { this.Value1 = Value; // кастомный оператор может все испортить } }; const CAST Union(Value); return(Union.Value2); #endif // TYPETOBYTES_FULL_SLOW } }; #define _C(A, B) CASTING::Casting(B) #define _R(A) TYPETOBYTES::Read(A) #define _W(A) A=TYPETOBYTES::Write(A) #define _OFFSET(A,B) TYPETOBYTES::Offset(A, A.B) #define _WRONG_ASSIGN_OPERATOR(A) TYPETOBYTES::IsWrongAssignOperator() #define EQUAL_DEFINE(A) \ template \ bool operator ==( const T1 &A ) const \ { \ return(::ArrayCompare(this.Bytes, \ _R(Value).Bytes) == 0); \ } \ \ template \ bool operator !=( const T1 &A ) const \ { \ return(!(this == Value)); \ } #define CONSTRUCTOR_DEFINE(A) \ template \ T1 operator []( const T1 Pos ) const \ { \ STRUCT_TYPE Res = {0}; \ const int iPos = (int)Pos; \ \ if (iPos + sizeof(T1) <= ::ArraySize(this.Bytes)) \ { \ STRUCT_READ Tmp = {0}; \ \ ::ArrayCopy(Tmp.Bytes, this.Bytes, 0, iPos, sizeof(T1)); \ \ Res = _C(STRUCT_TYPE, Tmp); \ } \ \ return(Res.Value); \ } \ \ string operator []( const string Pos ) const \ { \ string Res = NULL; \ const int iPos = (int)Pos; \ const int Size = ::ArraySize(this.Bytes); \ int iEnd = iPos; \ \ while ((iEnd < Size) && (this.Bytes[iEnd] != 0)) \ iEnd++; \ \ if (iPos < Size) \ { \ uchar TmpBytes[]; \ \ ::ArrayCopy(TmpBytes, this.Bytes, 0, iPos, iEnd - iPos); \ \ _W(Res) = TmpBytes; \ } \ \ return(Res); \ } \ \ EQUAL_DEFINE(Value) \ EQUAL_DEFINE(Value[]) struct STRUCT_READ_ARRAY { uchar Bytes[]; STRUCT_READ_ARRAY( void ) { } template STRUCT_READ_ARRAY( const T &Value[] ) { const int Size = ::ArraySize(Value); const int Step = sizeof(T); ::ArrayResize(this.Bytes, Size * Step); for (int i = 0, j = 0; i < Size; i++, j += Step) ::ArrayCopy(this.Bytes, _R(Value[i]).Bytes, j); } STRUCT_READ_ARRAY( const string &Value[] ) { const int Size = ::ArraySize(Value); for (int i = 0, j = 0; i < Size; j += ::StringLen(Value[i++]) + 1) ::ArrayCopy(this.Bytes, _R(Value[i]).Bytes, j); } STRUCT_READ_ARRAY( const string &Value ) { ::StringToCharArray(Value, this.Bytes); } void operator =( const STRUCT_READ_ARRAY &Value ) { ::ArrayCopy(this.Bytes, Value.Bytes); return; } CONSTRUCTOR_DEFINE(STRUCT_READ_ARRAY) }; #define DEFINE_TYPES \ M(char) M(short) M(int) M(long) M(uchar) M(ushort) M(uint) \ M(ulong) M(bool) M(double) M(float) M(color) M(datetime) template struct STRUCT_TYPE { private: template void Set( STRUCT_TYPE &Struct, const T1 &tValue ) const { Struct = _C(STRUCT_TYPE, tValue); } #define M(A) \ void Set( STRUCT_TYPE &Struct, const A tValue ) const \ { \ Struct.Value = tValue; \ } DEFINE_TYPES #undef M public: T Value; void operator =( const T& tValue ) { // Обходим возможный кастомный оператор присваивания у T-типа // https://www.mql5.com/ru/forum/1111/page1870#comment_4863733 this.Set(this, tValue); } }; template struct STRUCT_READ { uchar Bytes[sizeof(T)]; void operator =( const T &Value ) { const STRUCT_TYPE Res = Value; this = _C(STRUCT_READ, Res); } CONSTRUCTOR_DEFINE(STRUCT_READ) }; #undef CONSTRUCTOR_DEFINE #undef EQUAL_DEFINE template struct STRUCT_WRITE { private: T Data; int Pos; template T1 GetValue( const T1&, const uchar &Array[] ) const { STRUCT_READ Res = {0}; // Указание всех параметров, т.к. в MQL4 статические массивы могут менять размер ::ArrayCopy(Res.Bytes, Array, 0, 0, ::MathMin(::ArraySize(Array), ::ArraySize(Res.Bytes))); return(_C(STRUCT_TYPE, Res).Value); } string GetValue( const string&, const uchar &Array[] ) const { return(::CharArrayToString(Array)); } public: STRUCT_WRITE( const T &Value ) { this.Data = Value; this.Pos = 0; } STRUCT_WRITE operator []( const int iPos ) { this.Pos = iPos; return(this); } // Нужен для вышестоящей return(this) void operator =( const STRUCT_WRITE &Value ) { this.Data = Value.Data; this.Pos = Value.Pos; return; } // Нужен для вышестоящей return(this) STRUCT_WRITE( void ) { } #define OPERATOR_DEFINE(A ) \ template \ T operator =( const T1 A ) const \ { \ uchar TmpBytes[]; \ ::ArrayCopy(TmpBytes, _R(this.Data).Bytes); \ \ ::ArrayCopy(TmpBytes, _R(Value).Bytes, this.Pos); \ \ return(this.GetValue(this.Data, TmpBytes)); \ } OPERATOR_DEFINE(Value) OPERATOR_DEFINE(&Value) OPERATOR_DEFINE(&Value[]) #undef OPERATOR_DEFINE T operator =( const string &Value[] ) const { uchar TmpBytes[]; ::ArrayCopy(TmpBytes, _R(this.Data).Bytes); if (typename(T) == "string") // когда пишем строковый массив в строку, концы строк игнорируем { const int Size = ::ArraySize(Value); for (int i = 0, j = this.Pos; i < Size; j += ::StringLen(Value[i++])/* + 1*/) // Без единицы удобнее - игнорирование нулевых разделителей ::ArrayCopy(TmpBytes, _R(Value[i]).Bytes, j); } else ::ArrayCopy(TmpBytes, _R(Value).Bytes, this.Pos); return(this.GetValue(this.Data, TmpBytes)); } }; class TYPETOBYTES { private: template static const STRUCT_TYPE FillBytes( const uchar Byte = UCHAR_MAX ) { STRUCT_READ Res = {0}; ::ArrayInitialize(Res.Bytes, Byte); return(_C(STRUCT_TYPE, Res)); } public: #define READ_DEFINE(A, B, C) \ static const B Read( const C A ) \ { \ const B Res = Value; \ \ return(Res); \ } template READ_DEFINE(&Value, STRUCT_READ, T) template READ_DEFINE(&Value[], STRUCT_READ_ARRAY, T) #define M(A) READ_DEFINE(Value, STRUCT_READ, A) DEFINE_TYPES #undef M #undef READ_DEFINE #undef DEFINE_TYPES static const STRUCT_READ_ARRAY Read( const string Value ) { const STRUCT_READ_ARRAY Res(Value); return(Res); } template static STRUCT_WRITE Write( const T &Value ) { const STRUCT_WRITE Res(Value); return(Res); } template static bool IsWrongAssignOperator( void ) { bool Res = (sizeof(T) != 0); if (Res) { STRUCT_TYPE NotNull_Type = TYPETOBYTES::FillBytes(); STRUCT_TYPE Null_Type = {0}; T NotNull = NotNull_Type.Value; T Null = Null_Type.Value; // Переписать, когда исправят - https://www.mql5.com/ru/forum/1111/page1874#comment_4871926 // Причина таких действий - https://www.mql5.com/ru/forum/95447/page3#comment_4872456 const STRUCT_TYPE constNotNull_Type = NotNull_Type; const STRUCT_TYPE constNull_Type = Null_Type; const T constNotNull = constNotNull_Type.Value; const T constNull = constNull_Type.Value; Res = ((_R(Null) != Null_Type.Value) || (_R(NotNull) != NotNull_Type.Value) || (_R(Null) == NotNull) || (_R(constNull) != Null_Type.Value) || (_R(constNotNull) != NotNull_Type.Value) || (_R(constNull) == constNotNull)); } return(Res); } // Co-author - https://www.mql5.com/ru/users/alximiks // Return the first shift of Field bit in the Struct. template static int Offset( T1 &Struct, T2 &Field ) { int Res = -1; if (!TYPETOBYTES::IsWrongAssignOperator()) { const STRUCT_TYPE StoredField = Field; // Обходим T2-оператор присваивания // get Struct where Field value is with all unset bits (0000...) const STRUCT_TYPE Null = {0}; Field = Null.Value; const STRUCT_READ StructWithUnsetField = _C(STRUCT_READ, Struct); // get Struct where Field value is with all set bits (1111...) Field = TYPETOBYTES::FillBytes().Value; const STRUCT_READ StructWithSetField = _C(STRUCT_READ, Struct); //restore Struct data Field = StoredField.Value; for (int i = 0; i < sizeof(T1); i++) if (StructWithUnsetField.Bytes[i] != StructWithSetField.Bytes[i]) { Res = i; break; } } return(Res); } template static int Offset( T1 &Struct, T2 &Field[] ) { int Res = -1; if (!TYPETOBYTES::IsWrongAssignOperator()) { const STRUCT_TYPE StoredField = Field[0]; // Обходим T2-оператор присваивания // get Struct where Field value is with all unset bits (0000...) const STRUCT_TYPE Null = {0}; Field[0] = Null.Value; const STRUCT_READ StructWithUnsetField = _C(STRUCT_READ, Struct); // get Struct where Field value is with all set bits (1111...) Field[0] = TYPETOBYTES::FillBytes().Value; const STRUCT_READ StructWithSetField = _C(STRUCT_READ, Struct); //restore Struct data Field[0] = StoredField.Value; for (int i = 0; i < sizeof(T1); i++) if (StructWithUnsetField.Bytes[i] != StructWithSetField.Bytes[i]) { Res = i; break; } } return(Res); } }; // Побайтовый ArrayCopy, возврашает количество скопированных байтов template int _ArrayCopy( T1 &Dst_Array[], const T2 &Src_Array[], const int Dst_Start = 0, const int Src_Start = 0, const int Count = WHOLE_ARRAY ) { STRUCT_READ_ARRAY Dst(Dst_Array); const int Amount = _WRONG_ASSIGN_OPERATOR(T1) ? 0 : ::ArrayCopy(Dst.Bytes, _R(Src_Array).Bytes, Dst_Start, Src_Start, Count); if (Amount > 0) { const int Step = sizeof(T1); const int Size = ::ArraySize(Dst.Bytes); ::ArrayResize(Dst_Array, Size / Step + ((Size % Step == 0) ? 0 : 1)); const int TmpEnd = Dst_Start + Amount; const int End = ::MathMin(TmpEnd / Step + ((TmpEnd % Step == 0) ? 1 : 2), ::ArraySize(Dst_Array)); STRUCT_READ Tmp; for (int i = Dst_Start / Step, j = i * Step; i < End; i++, j += Step) { ::ArrayCopy(Tmp.Bytes, Dst.Bytes, 0, j, Step); Dst_Array[i] = _C(STRUCT_TYPE, Tmp).Value; } } return(Amount); } // Возврашает количество скопированных строк template int _ArrayCopy( string &Dst_Array[], const T &Src_Array[], const int Dst_Start = 0, const int Src_Start = 0, const int Count = WHOLE_ARRAY ) { const int Amount = ((Count == WHOLE_ARRAY) ? ::ArraySize(Src_Array) - Src_Start : Src_Start + Count); int Res = 0; if (Amount > 0) for (int i = Dst_Start, Pos = Src_Start; Pos < Amount ; i++) { if (i >= ::ArraySize(Dst_Array)) ::ArrayResize(Dst_Array, i + 1); _W(Dst_Array[i]) = _R(Src_Array)[(string)Pos]; Pos += ::StringLen(Dst_Array[i]) + 1; Res++; } return(Res); } template int _ArrayResize( T &Array[], const int Size ) { return(sizeof(T) * ::ArrayResize(Array, Size / sizeof(T) + ((bool)(Size % sizeof(T)) ? 1 : 0))); } // Заменяет побайтово кусок исходного массива на кусок другого массива. // Возвращает количество байтов новых данных. template int _ArrayReplace( T1 &Dst_Array[], const T2 &Src_Array[], const int Dst_Start = 0, const int Dst_Count = WHOLE_ARRAY, const int Src_Start = 0, const int Src_Count = WHOLE_ARRAY ) { uchar TmpData[]; const bool FlagTmpData = (Dst_Count != WHOLE_ARRAY); if (FlagTmpData) _ArrayCopy(TmpData, Dst_Array, 0, Dst_Start + Dst_Count); _ArrayResize(Dst_Array, Dst_Start); const int Res = _ArrayCopy(Dst_Array, Src_Array, Dst_Start, Src_Start, Src_Count); if (FlagTmpData) _ArrayCopy(Dst_Array, TmpData, Dst_Start + Res); return(Res); } template class CONTAINER { private: int AmountBytes; string Types[]; int Pos; int Amount; int GetPosBytes( void ) const { int Res = 0; for (int i = 0; i < this.Pos; i++) Res += _R(this.Data)[Res] + sizeof(int); return(Res); } public: T Data[]; CONTAINER( void ) : AmountBytes(0), Pos(-1), Amount(0) { } string GetType( void ) const { return(this.Types[(this.Pos == -1) ? 0 : this.Pos]); } CONTAINER* operator []( const int iPos ) { this.Pos = iPos; return(&this); } int GetAmount( void ) const { return(this.Amount); } #define OPERATOR_MACROS(A, B) \ template \ void operator =( const T1 A ) \ { \ uchar Bytes[]; \ \ _ArrayCopy(Bytes, _R(::ArraySize(_R(Value).Bytes)).Bytes); \ _ArrayCopy(Bytes, _R(Value).Bytes, ::ArraySize(Bytes)); \ \ if ((this.Pos >= this.Amount) || (this.Pos == -1)) \ { \ this.Amount = ::ArrayResize(this.Types, this.Amount + 1); \ this.Pos = this.Amount - 1; \ \ this.AmountBytes += _ArrayCopy(this.Data, Bytes, this.AmountBytes); \ } \ else \ { \ const int PosBytes = this.GetPosBytes(); \ const int Count = _R(this.Data)[PosBytes] + sizeof(int); \ \ this.AmountBytes += _ArrayReplace(this.Data, Bytes, PosBytes, Count) - Count; \ } \ \ this.Types[this.Pos] = typename(T1) + B; \ this.Pos = -1; \ \ return; \ } OPERATOR_MACROS(Value, "") OPERATOR_MACROS(&Value, "") OPERATOR_MACROS(&Value[], "[" + (string)::ArraySize(Value) + "]") #undef OPERATOR_MACROS /* template void operator =( CONTAINER &Container ) { this.AmountBytes = Container.AmountBytes; this.Amount = Container.Amount; ::ArrayCopy(this.Types, Container.Types); _ArrayCopy(this.Data, Container.Data); return; } */ template void Get( T1 &Value ) const { const int PosBytes = this.GetPosBytes(); const int Count = _R(this.Data)[PosBytes]; uchar Bytes[]; _ArrayCopy(Bytes, this.Data, 0, PosBytes + sizeof(int), Count); _W(Value) = Bytes; return; } template int Get( T1 &Value[] ) const { const int PosBytes = this.GetPosBytes(); const int Count = _R(this.Data)[PosBytes]; _ArrayCopy(Value, this.Data, 0, PosBytes + sizeof(int), Count); return(::ArraySize(Value)); } template T1 Get() // const // https://www.mql5.com/ru/forum/1111/page1948#comment_5461816 { T1 Res; this.Get(Res); return(Res); } };