23 Commits

Author SHA1 Message Date
Ding Li 802c86c9e3 Merge pull request #66 from biohazardxxx/patch-1
Update GlobalVariable.mqh
2023-12-19 10:40:40 +08:00
biohazardxxx 67aac378f5 Update GlobalVariable.mqh
When entering we delete any existing global variable with that name first. Otherwise this will cause a loop as GlobalVariable::makeTemp(m_name) will return false all the time.
2023-12-11 11:14:37 +01:00
Ding Li f9bf8d94a3 Fix more typo 2020-06-25 14:07:24 +08:00
Ding Li c8f16bf27d fix typo 2020-06-23 00:16:51 +08:00
lid 981394b9e9 fix #47: MQL5 update makes calling static methods of template type parameters impossible 2020-05-15 01:54:01 +08:00
lid 2dac5b433d fix #40: avoid name conflict by changing parameter name data to bytes 2019-09-29 09:35:27 +08:00
lid b3f817eda1 Add ToC and Donation section 2019-05-08 21:55:36 +08:00
lid 081500e0f9 Fix #14: PollItem aligns to 8 types on 64bit runtime 2019-05-08 21:05:11 +08:00
Ding Li 4468c27d30 Synchronize mql-zmq changes on Lang/Native; add MqlTick and MqlRates support for ZmqMsg 2018-02-12 09:33:43 +08:00
Ding Li df54353865 Add proper const modifier to import functions 2018-01-05 11:56:23 +08:00
Ding Li 66602232d6 Updated dependency mql4-lib; improved ZmqMsg setData method 2017-12-26 16:08:23 +08:00
Ding Li 5738f693d1 Add new DLLs compiled with VC2010 2017-12-18 10:04:08 +08:00
Ding Li afab5f93cc Released 1.5
This release contains important API changes that may break existing
code.

1. Incompatible change: `Socket.send` series methods are splitted
to `Socket.send` and `Socket.sendMore` and have more overloads for
common message types like string or empty
2. Remove PollItem duplicate API (#11)
3. Fix compiler warning (#10) and compile failure (#12)
4. Add RTReq example from ZMQ Guide Chapter 3
2017-10-28 22:45:23 +08:00
Ding Li 795acc13c8 Fix #12 (#1, #4, #9): SocketOptions curve key argument causes compile error 2017-10-23 22:13:23 +08:00
Ding Li 184b9f6d8b Merge branch 'master' of github.com:dingmaotu/mql-zmq 2017-10-23 10:22:36 +08:00
Ding Li 96fd26ad85 Fix #11: remove duplicate and deprecated API Socket::register 2017-10-23 10:21:36 +08:00
Ding Li 1d6b1aa2d5 Fix #11: remove duplicate and deprecated API Socket::register 2017-10-23 09:50:25 +08:00
Ding Li 07414d6300 Fix #10: fix size parameter type on 64bit Terminal 2017-10-17 09:29:06 +08:00
Ding Li 8475ecc03a Fix #8 by importing changes from mql4-lib (eliminating WinUser32.mqh dependency) 2017-09-13 11:52:21 +08:00
Ding Li 1d127c66b7 Merge pull request #7 from yerden/patch-1
zmq: fix typo in SendTimeout method
2017-09-08 08:31:13 +08:00
yerden 101807d69e zmq: fix typo in SendTimeout method 2017-09-08 00:27:47 +06:00
Ding Li cd7e5f0e47 Only resize target array when the array size is smaller than ZmqMsg 2017-08-30 10:15:46 +08:00
Ding Li bab1c9753a Fix documentation reference to a mql4-lib file 2017-08-18 10:34:30 +08:00
18 changed files with 509 additions and 221 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.
+147 -42
View File
@@ -25,21 +25,57 @@
class GlobalVariable
{
public:
static int total() {return GlobalVariablesTotal();}
static string name(int index) {return GlobalVariableName(index);}
static void flush() {GlobalVariablesFlush();}
static int total()
{
return GlobalVariablesTotal();
}
static string name(int index)
{
return GlobalVariableName(index);
}
static void flush()
{
GlobalVariablesFlush();
}
static bool exists(string name) {return GlobalVariableCheck(name);}
static datetime lastAccess(string name) {return GlobalVariableTime(name);}
static bool exists(string name)
{
return GlobalVariableCheck(name);
}
static datetime lastAccess(string name)
{
return GlobalVariableTime(name);
}
static bool makeTemp(string name) {return GlobalVariableTemp(name);}
static double get(string name) {return GlobalVariableGet(name);}
static bool get(string name,double &value) {return GlobalVariableGet(name,value);}
static datetime set(string name,double value) {return GlobalVariableSet(name,value);}
static bool setOn(string name,double value,double check) {return GlobalVariableSetOnCondition(name,value,check);}
static bool makeTemp(string name)
{
return GlobalVariableTemp(name);
}
static double get(string name)
{
return GlobalVariableGet(name);
}
static bool get(string name,double &value)
{
return (bool)GlobalVariableGet(name,value);
}
static bool set(string name,double value)
{
return (bool)GlobalVariableSet(name,value);
}
static bool setOn(string name,double value,double check)
{
return GlobalVariableSetOnCondition(name,value,check);
}
static bool remove(string name) {return GlobalVariableDel(name);}
static bool removeAll(string prefix=NULL,datetime before=0) {return GlobalVariablesDeleteAll(prefix,before);}
static bool remove(string name)
{
return (bool)GlobalVariableDel(name);
}
static bool removeAll(string prefix=NULL,datetime before=0)
{
return (bool)GlobalVariablesDeleteAll(prefix,before);
}
};
//+------------------------------------------------------------------+
//| TempVar is a variable whose life time is the same as the program |
@@ -57,14 +93,38 @@ public:
GlobalVariable::makeTemp(name);
}
}
~TempVar() {if(m_owned && isValid()) {GlobalVariable::remove(m_name);}}
~TempVar()
{
if(m_owned && isValid())
{
GlobalVariable::remove(m_name);
}
}
bool isValid() const {return GlobalVariable::exists(m_name);}
string getName() const {return m_name;}
bool set(double value) {return GlobalVariable::set(m_name,value);}
double get() const {return GlobalVariable::get(m_name);}
bool setOn(double value,double check) {return GlobalVariable::setOn(m_name,value,check);}
datetime lastAccess() const {return GlobalVariable::lastAccess(m_name);}
bool isValid() const
{
return GlobalVariable::exists(m_name);
}
string getName() const
{
return m_name;
}
bool set(double value)
{
return GlobalVariable::set(m_name,value);
}
double get() const
{
return GlobalVariable::get(m_name);
}
bool setOn(double value,double check)
{
return GlobalVariable::setOn(m_name,value,check);
}
datetime lastAccess() const
{
return GlobalVariable::lastAccess(m_name);
}
};
//+------------------------------------------------------------------+
//| |
@@ -77,7 +137,10 @@ public:
set(initial);
}
long increment(long by=1);
long decrement(long by=1) {return increment(-by);}
long decrement(long by=1)
{
return increment(-by);
}
};
//+------------------------------------------------------------------+
//| |
@@ -91,7 +154,7 @@ long AtomicVar::increment(long by)
value=(long)get();
success=setOn(value+by,value);
}
while(!success);
while(!success && !IsStopped());
return (value+by);
}
//+------------------------------------------------------------------+
@@ -103,7 +166,10 @@ private:
TempVar m_var;
public:
Semaphore(string name,long initial=0);
bool isValid() const {return m_var.isValid();}
bool isValid() const
{
return m_var.isValid();
}
bool acquire();
void release();
};
@@ -127,7 +193,8 @@ bool Semaphore::acquire(void)
do
{
long value=(long)m_var.get();
if(value == 0) return false;
if(value == 0)
return false;
success=m_var.setOn(value-1,value);
}
while(!success && !IsStopped());
@@ -168,22 +235,47 @@ class CriticalSection
private:
const string m_name;
public:
CriticalSection(string name):m_name(name){}
CriticalSection(string name):m_name(name) {}
bool isValid() const {return m_name!=NULL;}
string getName() const {return m_name;}
bool isValid() const
{
return m_name!=NULL;
}
string getName() const
{
return m_name;
}
void enter() { while(!GlobalVariable::makeTemp(m_name) && !IsStopped())Sleep(100); }
bool tryEnter() { return GlobalVariable::makeTemp(m_name); }
void leave() { GlobalVariable::remove(m_name);}
void enter()
{
GlobalVariable::remove(m_name);
while(!GlobalVariable::makeTemp(m_name) && !IsStopped())
Sleep(100);
}
bool tryEnter()
{
return GlobalVariable::makeTemp(m_name);
}
void leave()
{
GlobalVariable::remove(m_name);
}
};
//+------------------------------------------------------------------+
//| HandleManager should implement 2 methods: create & destroy |
//+------------------------------------------------------------------+
template<typename T>
interface HandleManager
{
T create();
void destroy(T);
};
//+------------------------------------------------------------------+
//| A reference counted global pointer (or handle) |
//| Generic type parameter T can be long or int depending the handle |
//| length (64bit or 32bit) |
//| HandleManager should implement 2 static methods: create & destroy|
//+------------------------------------------------------------------+
template<typename T,typename HandleManager>
template<typename T,typename HM>
class GlobalHandle
{
private:
@@ -192,12 +284,15 @@ private:
string m_counterName;
protected:
T m_ref;
HandleManager<T> *m_hm;
public:
GlobalHandle(string sharedKey=NULL):m_cs(sharedKey)
{
m_refName=m_cs.getName()+"_Ref";
m_counterName=m_cs.getName()+"_Count";
if(!m_cs.isValid()) m_ref=HandleManager::create();
m_hm = new HM;
if(!m_cs.isValid())
m_ref = m_hm.create();
else
{
m_cs.enter();
@@ -208,7 +303,7 @@ public:
}
if(long(GlobalVariable::get(m_counterName))==0)
{
m_ref=HandleManager::create();
m_ref = m_hm.create();
if(!GlobalVariable::exists(m_refName))
{
GlobalVariable::makeTemp(m_refName);
@@ -225,18 +320,28 @@ public:
}
~GlobalHandle()
{
if(!m_cs.isValid()) {HandleManager::destroy(m_ref); return;}
m_cs.enter();
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1);
if(long(GlobalVariable::get(m_counterName))==0)
if(!m_cs.isValid())
{
HandleManager::destroy(m_ref);
GlobalVariable::remove(m_refName);
GlobalVariable::remove(m_counterName);
m_hm.destroy(m_ref);
}
m_cs.leave();
else
{
m_cs.enter();
GlobalVariable::set(m_counterName,GlobalVariable::get(m_counterName)-1);
if(long(GlobalVariable::get(m_counterName))==0)
{
m_hm.destroy(m_ref);
GlobalVariable::remove(m_refName);
GlobalVariable::remove(m_counterName);
}
m_cs.leave();
}
delete m_hm;
}
T ref() const {return m_ref;}
T ref() const
{
return m_ref;
}
};
//+------------------------------------------------------------------+
+60 -17
View File
@@ -19,14 +19,7 @@
//| and limitations under the License. |
//+------------------------------------------------------------------+
#property strict
#import "stdlib.ex4"
string ErrorDescription(int error_code);
int RGB(int red_value,int green_value,int blue_value);
bool CompareDoubles(double number1,double number2);
string DoubleToStrMorePrecision(double number,int precision);
string IntegerToHexString(int integer_number);
#import
#include "Error.mqh"
//+------------------------------------------------------------------+
//| Mql language specific methods |
//+------------------------------------------------------------------+
@@ -34,13 +27,29 @@ class Mql
{
public:
static int getLastError() {return GetLastError();}
static string getErrorMessage(int errorCode) {return ErrorDescription(errorCode);}
static string getErrorMessage(int errorCode) {return GetErrorDescription(errorCode);}
static string doubleToString(double value,int precision) {return DoubleToStrMorePrecision(value,precision);}
static string integerToHexString(int value) {return IntegerToHexString(value);}
static int rgb(int red,int green,int blue) {return RGB(red,green,blue);}
//--- Prefer global DoubleToString function
static string doubleToString(double value,int precision) {return DoubleToString(value,precision);}
//--- Adapted from stdlib.mq4 by using `StringSetCharacter` instead of `StringSetChar`
static string integerToHexString(int value)
{
static const string digits="0123456789ABCDEF";
string hex="00000000";
int digit,shift=28;
for(int i=0; i<8; i++)
{
digit=(value>>shift)&0x0F;
StringSetCharacter(hex,i,digits[digit]);
shift-=4;
}
return(hex);
}
//--- Prefer Canvas/Canvas.mqh XRGB
static int rgb(int r,int g,int b) {return int(0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b));}
static bool isEqual(double a,double b) {return CompareDoubles(a,b);}
//--- Prefer Lang/Number.mqh Double::IsEqual
static bool isEqual(double a,double b) {return NormalizeDouble(a-b,8)==0;}
static bool isStopped() {return IsStopped();}
@@ -67,7 +76,19 @@ public:
static string getProgramName() {return MQLInfoString(MQL_PROGRAM_NAME);}
static string getProgramPath() {return MQLInfoString(MQL_PROGRAM_PATH);}
};
//+------------------------------------------------------------------+
//| Object getter/setter generator |
//| ObjectAttr generates: |
//| 1. private member m_property |
//| 2. public method setProperty |
//| 3. public method getProperty |
//| ObjectAttrBool is specific to boolean type properties: |
//| 1. private member m_isProperty |
//| 2. public method setProperty |
//| 3. public method isProperty |
//| Use *Read or *Write versions for read only or write only |
//| properties |
//+------------------------------------------------------------------+
#define ObjectAttr(Type, Private, Public) \
public:\
Type get##Public() const {return m_##Private;}\
@@ -87,13 +108,35 @@ public:\
private:\
Type m_##Private\
#define ObjectAttrBool(Public) \
public:\
bool is##Public() const {return m_is##Public;}\
void set##Public(bool value) {m_is##Public=value;}\
private:\
bool m_is##Public\
#define ObjectAttrBoolRead(Public) \
public:\
bool is##Public() const {return m_is##Public;}\
private:\
bool m_is##Public\
#define ObjectAttrBoolWrite(Public) \
public:\
void set##Public(bool value) {m_is##Public=value;}\
private:\
bool m_##Private\
//+------------------------------------------------------------------+
//| Print debug messages: only generate code for debugging runs |
//+------------------------------------------------------------------+
#ifdef _DEBUG
#define Debug(msg) Print(">>> DEBUG: In ",__FUNCTION__,"(",__FILE__,":",__LINE__,") [", msg, "]")
#define Debug(msg) PrintFormat(">>> DEBUG[%s,%d,%s]: %s",__FILE__,__LINE__,__FUNCTION__,msg);
#else
#define Debug(msg)
#endif
//--- Execute some code in the global scope
//+------------------------------------------------------------------+
//| Execute some code in the global scope |
//+------------------------------------------------------------------+
#define BEGIN_EXECUTE(Name) class __Execute##Name\
{\
public:__Execute##Name()\
+34 -57
View File
@@ -45,31 +45,13 @@
#define size_t int
#endif
//--- _WIN32_WINNT version constants
#define _WIN32_WINNT_NT4 0x0400 // Windows NT 4.0
#define _WIN32_WINNT_WIN2K 0x0500 // Windows 2000
#define _WIN32_WINNT_WINXP 0x0501 // Windows XP
#define _WIN32_WINNT_WS03 0x0502 // Windows Server 2003
#define _WIN32_WINNT_WIN6 0x0600 // Windows Vista
#define _WIN32_WINNT_VISTA 0x0600 // Windows Vista
#define _WIN32_WINNT_WS08 0x0600 // Windows Server 2008
#define _WIN32_WINNT_LONGHORN 0x0600 // Windows Vista
#define _WIN32_WINNT_WIN7 0x0601 // Windows 7
#define _WIN32_WINNT_WIN8 0x0602 // Windows 8
#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1
#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 // Windows 10
#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10
//--- define you own for your target platform
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
#import "kernel32.dll"
void RtlMoveMemory(intptr_t dest,const uchar &array[],size_t length);
void RtlMoveMemory(uchar &array[],intptr_t src,size_t length);
void RtlMoveMemory(intptr_t dest,const MqlRates &array[],size_t length);
void RtlMoveMemory(MqlRates &array[],intptr_t src,size_t length);
void RtlMoveMemory(intptr_t dest,const MqlTick &value,size_t length);
void RtlMoveMemory(MqlTick &value,intptr_t src,size_t length);
void RtlMoveMemory(intptr_t &dest,intptr_t src,size_t length);
int lstrlen(intptr_t psz);
int lstrlenW(intptr_t psz);
@@ -83,34 +65,45 @@ int MultiByteToWideChar(uint codePage,
string &str,
int length
);
uint FormatMessageW(uint dwFlags,
intptr_t lpSource,
uint dwMessageId,
uint dwLanguageId,
ushort &buffer[],
uint nSize,
intptr_t Arguments
);
#import
//--- This is a standard header of the official MetaTrader distribution
#include <WinUser32.mqh>
//+------------------------------------------------------------------+
//| Copy the memory contents pointed by src to array |
//| array parameter should be initialized to the desired size |
//| Need to import RtlMoveMemory with appropriate parameter type T |
//+------------------------------------------------------------------+
void ArrayFromPointer(uchar &array[],intptr_t src,int count=WHOLE_ARRAY)
template<typename T>
void ArrayFromPointer(T &array[],intptr_t src,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(array,src,(size_t)size);
RtlMoveMemory(array,src,(size_t)(size*sizeof(T)));
}
//+------------------------------------------------------------------+
//| Copy array to the memory pointed by dest |
//| Need to import RtlMoveMemory with appropriate parameter type T |
//+------------------------------------------------------------------+
void ArrayToPointer(const uchar &array[],intptr_t dest,int count=WHOLE_ARRAY)
template<typename T>
void ArrayToPointer(const T &array[],intptr_t dest,int count=WHOLE_ARRAY)
{
int size=(count==WHOLE_ARRAY)?ArraySize(array):count;
RtlMoveMemory(dest,array,(size_t)size);
RtlMoveMemory(dest,array,(size_t)(size*sizeof(T)));
}
//+------------------------------------------------------------------+
//| Copy the memory contents pointed by src to value type T |
//| Need to import RtlMoveMemory with appropriate parameter type T |
//+------------------------------------------------------------------+
template<typename T>
void ValueFromPointer(T &value,intptr_t src)
{
RtlMoveMemory(value,src,(size_t)sizeof(T));
}
//+------------------------------------------------------------------+
//| Copy value to the memory pointed by dest |
//| Need to import RtlMoveMemory with appropriate parameter type T |
//+------------------------------------------------------------------+
template<typename T>
void ValueToPointer(const T &value,intptr_t dest)
{
RtlMoveMemory(dest,value,(size_t)sizeof(T));
}
//+------------------------------------------------------------------+
//| For void** type, dereference a level to void* |
@@ -150,16 +143,10 @@ string StringFromUtf8Pointer(intptr_t psz,int len)
if(len < 0) return NULL;
string res;
int required=MultiByteToWideChar(CP_UTF8,0,psz,len,res,0);
StringInit(res,required);
int resLength = MultiByteToWideChar(CP_UTF8,0,psz,len,res,required);
if(resLength != required)
{
return NULL;
}
else
{
return res;
}
// need to include the NULL terminating character
StringInit(res,required+1);
int rlen=MultiByteToWideChar(CP_UTF8,0,psz,len,res,required);
return rlen != required ? NULL : res;
}
//+------------------------------------------------------------------+
//| for null-terminated string |
@@ -188,13 +175,3 @@ void StringToUtf8(const string str,uchar &utf8[],bool ending=true)
StringToCharArray(str,utf8,0,count,CP_UTF8);
}
//+------------------------------------------------------------------+
//| Get system defined error code message |
//+------------------------------------------------------------------+
string GetErrorMessage(int errorCode)
{
static ushort buffer[64*1024];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
0,errorCode,0,buffer,ArraySize(buffer),0);
return ShortArrayToString(buffer);
}
//+------------------------------------------------------------------+
+21 -7
View File
@@ -45,6 +45,20 @@ int zmq_ctx_shutdown(intptr_t context);
int zmq_ctx_set(intptr_t context,int option,int optval);
int zmq_ctx_get(intptr_t context,int option);
#import
class ContextHandleManager: public HandleManager<intptr_t>
{
intptr_t create() override
{
return zmq_ctx_new();
}
void destroy(intptr_t handle) override
{
if(0!=zmq_ctx_term(handle))
{
Debug("failed to terminate context");
}
}
};
//+------------------------------------------------------------------+
//| Wraps a 0MZ context |
//| |
@@ -64,7 +78,7 @@ int zmq_ctx_get(intptr_t context,int option);
//| and in a manner not easily recognized by humans, for example: |
//| "__3kewducdxhkd__" |
//+------------------------------------------------------------------+
class Context: public GlobalHandle<intptr_t,Context>
class Context: public GlobalHandle<intptr_t,ContextHandleManager>
{
protected:
int get(int option) {return zmq_ctx_get(m_ref,option);}
@@ -75,26 +89,26 @@ public:
static intptr_t create() {return zmq_ctx_new();}
static void destroy(intptr_t handle) {if(0!=zmq_ctx_term(handle)) {Debug("failed to terminate context");}}
Context(string shared=NULL):GlobalHandle<intptr_t,Context>(shared) {}
Context(string shared=NULL):GlobalHandle<intptr_t,ContextHandleManager>(shared) {}
bool shutdown() {return 0==zmq_ctx_shutdown(m_ref);}
int getIoThreads() {return get(ZMQ_IO_THREADS);}
void setIoThreads(int value) {if(!set(ZMQ_IO_THREADS,value)){Debug("failed to set ZMQ_IO_THREADS");}}
void setIoThreads(int value) {if(!set(ZMQ_IO_THREADS,value)) {Debug("failed to set ZMQ_IO_THREADS");}}
int getMaxSockets() {return get(ZMQ_MAX_SOCKETS);}
void setMaxSockets(int value) {if(!set(ZMQ_MAX_SOCKETS,value)){Debug("failed to set ZMQ_MAX_SOCKETS");}}
void setMaxSockets(int value) {if(!set(ZMQ_MAX_SOCKETS,value)) {Debug("failed to set ZMQ_MAX_SOCKETS");}}
int getMaxMessageSize() {return get(ZMQ_MAX_MSGSZ);}
void setMaxMessageSize(int value) {if(!set(ZMQ_MAX_MSGSZ,value)){Debug("failed to set ZMQ_MAX_MSGSZ");}}
void setMaxMessageSize(int value) {if(!set(ZMQ_MAX_MSGSZ,value)) {Debug("failed to set ZMQ_MAX_MSGSZ");}}
int getSocketLimit() {return get(ZMQ_SOCKET_LIMIT);}
int getIpv6Options() {return get(ZMQ_IPV6);}
void setIpv6Options(int value) {if(!set(ZMQ_IPV6,value)){Debug("failed to set ZMQ_IPV6");}}
void setIpv6Options(int value) {if(!set(ZMQ_IPV6,value)) {Debug("failed to set ZMQ_IPV6");}}
bool isBlocky() {return 1==get(ZMQ_BLOCKY);}
void setBlocky(bool value) {if(!set(ZMQ_BLOCKY,value?1:0)){Debug("failed to set ZMQ_BLOCKY");}}
void setBlocky(bool value) {if(!set(ZMQ_BLOCKY,value?1:0)) {Debug("failed to set ZMQ_BLOCKY");}}
//--- Following options is not supported on windows
void setSchedulingPolicy(int value) {/*ZMQ_THREAD_SCHED_POLICY*/}
+30 -76
View File
@@ -79,6 +79,12 @@ struct PollItem
uintptr_t fd;
short events;
short revents;
#ifdef __X64__
//--- note here that if the program runs on 64bit runtime, we need to pad the
//--- struct to align to 8 bytes
//--- thanks https://github.com/feng-ye for the solution to https://github.com/dingmaotu/mql-zmq/issues/14
int __unused;
#endif
bool hasInput() const {return(revents&ZMQ_POLLIN)!=0;}
bool hasOutput() const {return(revents&ZMQ_POLLOUT)!=0;}
@@ -133,16 +139,32 @@ public:
bool connect(string addr);
bool disconnect(string addr);
//--- send and receive packets
bool recv(uchar &buf[],bool nowait=false);
bool send(const uchar &buf[],bool nowait=false,bool more=false);
bool sendConst(const uchar &buf[],bool nowait=false,bool more=false);
//--- send raw bytes
bool send(const uchar &buf[],bool nowait=false) {return -1!=zmq_send(m_ref,buf,ArraySize(buf),nowait?ZMQ_DONTWAIT:0);}
bool sendConst(const uchar &buf[],bool nowait=false) {return -1!=zmq_send_const(m_ref,buf,ArraySize(buf),nowait?ZMQ_DONTWAIT:0);}
bool send(ZmqMsg &msg,bool nowait=false,bool more=false);
bool recv(ZmqMsg &msg,bool nowait=false);
//--- send ZmqMsg
bool send(ZmqMsg &msg,bool nowait=false) {return -1!=zmq_msg_send(msg,m_ref,nowait?ZMQ_DONTWAIT:0);}
//--- send string
bool send(string msg,bool nowait=false) {ZmqMsg m(msg); return send(m,nowait);}
//--- send empty
bool send(bool nowait=false) {ZmqMsg m; return send(m,nowait);}
void register(PollItem &pollitem,bool read=false,bool write=false);
void register(PollItem &pollitems[],int index,bool read=false,bool write=false);
//--- same as above 5 but for multipart messages
bool sendMore(const uchar &buf[],bool nowait=false);
bool sendConstMore(const uchar &buf[],bool nowait=false);
bool sendMore(ZmqMsg &msg,bool nowait=false)
{
int flags=ZMQ_SNDMORE;
if(nowait) flags|=ZMQ_DONTWAIT;
return -1!=zmq_msg_send(msg,m_ref,flags);
}
bool sendMore(string msg,bool nowait=false) {ZmqMsg m(msg); return sendMore(m,nowait);}
bool sendMore(bool nowait=false) {ZmqMsg m; return sendMore(m,nowait);}
//--- receive packets
bool recv(uchar &buf[],bool nowait=false) {return -1!=zmq_recv(m_ref,buf,ArraySize(buf),nowait?ZMQ_DONTWAIT:0);}
bool recv(ZmqMsg &msg,bool nowait=false) {return -1!=zmq_msg_recv(msg,m_ref,nowait?ZMQ_DONTWAIT:0);}
//--- monitor socket events
bool monitor(string addr,int events);
@@ -204,54 +226,6 @@ bool Socket::disconnect(string addr)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Socket::recv(uchar &buf[],bool nowait=false)
{
int options=0;
if(nowait) options|=ZMQ_DONTWAIT;
return -1!=zmq_recv(m_ref,buf,ArraySize(buf),options);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Socket::send(const uchar &buf[],bool nowait=false,bool more=false)
{
int options=0;
if(nowait) options|=ZMQ_DONTWAIT;
if(more) options|=ZMQ_SNDMORE;
return -1!=zmq_send(m_ref,buf,ArraySize(buf),options);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Socket::sendConst(const uchar &buf[],bool nowait=false,bool more=false)
{
int options=0;
if(nowait) options|=ZMQ_DONTWAIT;
if(more) options|=ZMQ_SNDMORE;
return -1!=zmq_send_const(m_ref,buf,ArraySize(buf),options);
}
//+------------------------------------------------------------------+
//| Send a zmq_msg_t through a socket |
//+------------------------------------------------------------------+
bool Socket::send(ZmqMsg &msg,bool nowait=false,bool more=false)
{
int flags=0;
if(nowait) flags|=ZMQ_DONTWAIT;
if(more) flags|=ZMQ_SNDMORE;
return -1!=zmq_msg_send(msg,m_ref,flags);
}
//+------------------------------------------------------------------+
//| Receive a zmq_msg_t from a socket |
//+------------------------------------------------------------------+
bool Socket::recv(ZmqMsg &msg,bool nowait=false)
{
int flags=0;
if(nowait) flags|=ZMQ_DONTWAIT;
return -1!=zmq_msg_recv(msg,m_ref,flags);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Socket::monitor(string addr,int events)
{
uchar str[];
@@ -263,26 +237,6 @@ bool Socket::monitor(string addr,int events)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Socket::register(PollItem &pollitem,bool read=false,bool write=false)
{
ZeroMemory(pollitem);
pollitem.socket=m_ref;
if(read) pollitem.events|=ZMQ_POLLIN;
if(write) pollitem.events|=ZMQ_POLLOUT;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void Socket::register(PollItem &pollitems[],int index,bool read=false,bool write=false)
{
ZeroMemory(pollitems[index]);
pollitems[index].socket=m_ref;
if(read) pollitems[index].events|=ZMQ_POLLIN;
if(write) pollitems[index].events|=ZMQ_POLLOUT;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool Socket::proxy(Socket *frontend,Socket *backend,Socket *capture)
{
intptr_t frontend_ref= CheckPointer(frontend)==POINTER_DYNAMIC?frontend.ref():0;
+6 -3
View File
@@ -184,10 +184,13 @@ public:
SOCKOPT_GET_BYTES(OptionName,Macro,InitSize)
//--- for curve key
//--- NOTE that the length of the key array must be 32.
//--- since some versions of mt4 and newer versions of mt5 will report error
//--- when the length of the array is specified, so the length specification is removed
#define SOCKOPT_CURVE_KEY(KeyType,Macro) \
bool getCurve##KeyType##Key(uchar &key[32]) {size_t len=32; return getOption(Macro,key,len);} \
bool getCurve##KeyType##Key(uchar &key[]) {size_t len=32; return getOption(Macro,key,len);} \
bool getCurve##KeyType##Key(string &key) {return getStringOption(Macro,key,41);} \
bool setCurve##KeyType##Key(const uchar &key[32]) {return setOption(Macro,key,32);} \
bool setCurve##KeyType##Key(const uchar &key[]) {return setOption(Macro,key,32);} \
bool setCurve##KeyType##Key(string key) {return setStringOption(Macro,key);}
SOCKOPT_GET(int,Type,ZMQ_TYPE)
@@ -251,7 +254,7 @@ public:
SOCKOPT(int,ReceiveTimeout,ZMQ_RCVTIMEO) // milliseconds
SOCKOPT(int,SendBuffer,ZMQ_SNDBUF) // bytes
SOCKOPT(int,SendHighWaterMark,ZMQ_SNDHWM) // messages
SOCKOPT(int,SendTimout,ZMQ_SNDTIMEO)
SOCKOPT(int,SendTimeout,ZMQ_SNDTIMEO)
SOCKOPT_GET_BOOL(ReceiveMore,ZMQ_RCVMORE)
+7 -7
View File
@@ -88,9 +88,9 @@ public:
size_t size() {return zmq_msg_size(this);}
void getData(uchar &data[]);
void getData(uchar &bytes[]);
string getData();
void setData(const uchar &data[]);
void setData(const uchar &bytes[]);
bool more() {return 1==zmq_msg_more(this);}
@@ -113,12 +113,12 @@ bool ZmqMsg::setStringData(string data,bool nullterminated)
//+------------------------------------------------------------------+
//| Get message data as bytes array |
//+------------------------------------------------------------------+
void ZmqMsg::getData(uchar &data[])
void ZmqMsg::getData(uchar &bytes[])
{
size_t size=size();
intptr_t src=data();
ArrayResize(data,(int)size);
ArrayFromPointer(data,src);
if(ArraySize(bytes)<size) ArrayResize(bytes,(int)size);
ArrayFromPointer(bytes,src);
}
//+------------------------------------------------------------------+
//| Get message data as utf-8 string |
@@ -132,11 +132,11 @@ string ZmqMsg::getData()
//+------------------------------------------------------------------+
//| copy data to message internal storage |
//+------------------------------------------------------------------+
void ZmqMsg::setData(const uchar &data[])
void ZmqMsg::setData(const uchar &bytes[])
{
intptr_t dest=data();
size_t size=size();
ArrayToPointer(data,dest,size);
ArrayToPointer(bytes,dest,(int)size);
}
//+------------------------------------------------------------------+
//| Wraps zmq_msg_gets: get metadata associated with the msg |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+41 -6
View File
@@ -2,6 +2,15 @@
ZMQ binding for the MQL language (both 32bit MT4 and 64bit MT5)
* [1. Introduction](#introduction)
* [2. Files and Installation](#files-and-installation)
* [3. About string encoding](#about-string-encoding)
* [4. Notes on context creation](#notes-on-context-creation)
* [5. Usage](#usage)
* [6. TODO](#todo)
* [7. Changes](#changes)
* [8. Donation](#donation)
## Introduction
This is a complete binding of the [ZeroMQ](http://zeromq.org/) library
@@ -39,13 +48,13 @@ This binding contains three sets of files:
use them in MetaTrader5.
3. Precompiled DLLs of both 64bit (`Library/MT5`) and 32bit (`Library/MT4`)
ZeroMQ and libsodium are provided. Copy the corresponding DLLs to the
`Library` folder of your MetaTrader terminal. If you are using MT5 32bit, use
the 32bit version from `Library/MT4`. **The DLLs require that you have the
latest Visual C++ runtime (2015)**.
ZeroMQ (4.2.0) and libsodium (1.0.11) are provided. Copy the corresponding
DLLs to the `Library` folder of your MetaTrader terminal. If you are using
MT5 32bit, use the 32bit version from `Library/MT4`. **The DLLs require that
you have the latest Visual C++ runtime (2015)**.
*Note* that if you are using **MT5 32bit**, you need to comment out the
`__X64__` macro definition at the top of the `Include/Zmq/Common.mqh`. I
`__X64__` macro definition at the top of the `Include/Mql/Lang/Native.mqh`. I
assume MT5 is 64 bit, since their is no way to detect 32 bit by native
macros, and to define pointer related values a macro like this is required.
@@ -54,6 +63,19 @@ This binding contains three sets of files:
`libsodium.dll` is copied from the official binary release. If you want to
support security mechanisms other than `curve`, or you want to use transports
like OpenPGM, you need to compile your own DLL.
*Note* for WINE users, if the default binaries do not work for you, you can
try the binaries in the `Library/VC2010` directory. The new binaries are a
little newer (libzmq 4.2.2 and libsodium 1.0.36). They are compiled with
Visual C++ 2010 Express SP1 (using the Windows SDK 7.1), and supposed to be
more compatible to WINE than the VS2015 version. They depend on VC2010
runtime (msvcr100.dll and msvcp100.dll). I have actually tested the old and
the new DLLs on WINE 2.0.3 (Debian Jessie PlayOnLinux 32bit with MetaTrader4
build 1090) and they both work. So it is *not* guarenteed but it is nice to
have an alternative. The new libzmq.dll only runs on vista or newer windows
because I turned on the `using poll` option. This improves performance a
little bit. Since MetaTrader4 officially no longer supports Windows XP, I
assume this would not be a problem.
## About string encoding
@@ -128,14 +150,27 @@ void OnStart()
1. Write more tests.
2. Add more examples from the official ZMQ guide.
3. More documentation
4. High level API for common patterns
## Changes
* 2017-10-28: Released 1.5: Important: API change for `Socket.send`; Remove
PollItem duplicate API (#11); Fix compiler warning (#10) and compile failure
(#12); Add RTReq example from ZMQ Guide Chapter 3.
* 2017-08-18: Released 1.4: Fix ZmqMsg setData bug; Change License to Apache
2.0; Inlcude mql4-lib dependencies directly.
* 2017-07-18: Released 1.3: Refactored poll support; Add Chapter 2 examples from
the official ZMQ guide.
* 2017-06-08: Released 1.2: Fix GlobalHandle bug; Add rebuild method to ZmqMsg;
Complete all examples in ZMQ Guide Chanpter 1.
Complete all examples in ZMQ Guide Chapter 1.
* 2017-05-26: Released 1.1: add the ability to share a ZMQ context globally in a terminal
* 2016-12-27: Released 1.0.
## Donation
This binding is created in spare time and the author answers questions, solves issues, and intends to maintain the code so that bugs are fixed and the binding is kept up to date with official releases. If you think that the binding is useful to you or your organization, please consider donate to the author for his effort to maintain this binding. Thanks!
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RL6U2HFVTCUUN)
![alipay](ALIPAY.JPG)
@@ -42,8 +42,8 @@ void OnStart()
do
{
frontend.recv(message);
more=message.more();
backend.send(message,false,more);
if(message.more()) backend.sendMore(message);
else backend.send(message);
}
while(more);
}
@@ -53,8 +53,8 @@ void OnStart()
do
{
backend.recv(message);
more=message.more();
frontend.send(message,false,more);
if(message.more()) frontend.sendMore(message);
else frontend.send(message);
}
while(more);
}
@@ -38,8 +38,8 @@ void OnStart()
do
{
frontend.recv(message);
more=message.more();
backend.send(message,false,more);
if(message.more()) backend.sendMore(message);
else backend.send(message);
}
while(more);
}
@@ -0,0 +1,85 @@
//+------------------------------------------------------------------+
//| RTReqBroker.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#property show_inputs
//+------------------------------------------------------------------+
//| This example comes from the "Load Balancing Pattern" |
//| The original example creates threads for workers and spawn them |
//| in the broker main thread. MetaTrader Terminal does not support |
//| thread creation. So we split the broker and worker code to two |
//| scripts. Since we splitted the code, we need to wait all workers |
//| connect. |
//| This is the broker part. |
//+------------------------------------------------------------------+
#include <Zmq/Zmq.mqh>
input int InpNumberWorkers=5;
//+------------------------------------------------------------------+
//| Custom routing Router to Mama (ROUTER to REQ) (adapted from C++) |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
Context context("rtreq");
Socket broker(context,ZMQ_ROUTER);
broker.bind("inproc://rtreq");
string identities[];
ArrayResize(identities,InpNumberWorkers);
// Wait until InpNumberWorkers workers connected
for(int i=0; i<InpNumberWorkers; i++)
{
ZmqMsg msg;
broker.recv(msg);
string identity=msg.getData();
broker.recv(msg); // Envelope delimiter
broker.recv(msg); // Response from worker
identities[i]=identity;
Print("Broker: Worker ",identity," connected.");
}
Print("Broker: All workers connected!");
// Notify all workers that it is ready to dispatch work
for(int i=0; i<InpNumberWorkers; i++)
{
broker.sendMore(identities[i]);
broker.sendMore();
broker.send("Go!");
}
// Run for five seconds and then tell workers to end
long endTime=TimeLocal()+5;
int workersFired=0;
while(!IsStopped())
{
ZmqMsg msg;
// Next message gives us least recently used worker
broker.recv(msg);
string identity=msg.getData();
Print("Broker: Get available worker [",identity,"]");
broker.recv(msg); // Envelope delimiter
broker.recv(msg); // Response from worker
Print("Broker: And he says ",msg.getData());
if(!broker.sendMore(identity)) {Print("Error sending identity.");}
if(!broker.sendMore("")) {Print("Error sending delimeter.");}
// Encourage workers until it's time to fire them
if(TimeLocal()<endTime)
{
Print("Send work!");
if(!broker.send("Work harder")) {Print("Error sending work.");}
}
else
{
Print("Send fire!");
if(!broker.send("Fired!")) {Print("Error sending fired.");}
if(++workersFired==InpNumberWorkers)
break;
}
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,72 @@
//+------------------------------------------------------------------+
//| RTReqWorker.mq4 |
//| Copyright 2017, Li Ding |
//| dingmaotu@126.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Li Ding"
#property link "dingmaotu@126.com"
#property version "1.00"
#property strict
#include <Zmq/Zmq.mqh>
#property show_inputs
//+------------------------------------------------------------------+
//| This example comes from the "Load Balancing Pattern" |
//| The original example creates threads for workers and spawn them |
//| in the broker main thread. MetaTrader Terminal does not support |
//| thread creation. So we split the broker and worker code to two |
//| scripts. Since we splitted the code, we need to wait all workers |
//| connect. |
//| This is the worker part. |
//| For the worker, we can use either ZMQ_REQ or ZMQ_DEALER, the |
//| difference is minimal. When using a dealer socket, remember to |
//| send an empty frame to emulate the REQ behavior. |
//+------------------------------------------------------------------+
#define within(num) (int) ((float) num * MathRand() / (32767 + 1.0))
input string InpWorkerIdentity="worker1";
//+------------------------------------------------------------------+
//| Custom routing Router to Mama (ROUTER to REQ) (adapted from C++) |
//| The worker |
//| Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> |
//+------------------------------------------------------------------+
void OnStart()
{
//--- use inproc
Context context("rtreq");
Socket worker(context,ZMQ_REQ);
//--- We use a string identity for ease here
worker.setIdentity(InpWorkerIdentity);
worker.connect("inproc://rtreq");
ZmqMsg msg("Connect!");
worker.send(msg);
worker.recv(msg);
Print(InpWorkerIdentity," connect: ",msg.getData());
int total=0;
while(!IsStopped())
{
// Tell the broker we're ready for work
worker.send("Hi Boss");
// Get workload from broker, until finished
worker.recv(msg);
string workload=msg.getData();
if("Fired!"==workload)
{
Print(InpWorkerIdentity," is fired!");
Print(InpWorkerIdentity," processed: ",total," tasks");
break;
}
else
{
Print(InpWorkerIdentity," received work!");
}
total++;
// Do some random work
Sleep(within(500)+1);
}
}
//+------------------------------------------------------------------+