Prior to any call to ATcl, the functions ATcl_OnInit () and at the end of the script / indicator / advisor ATcl_OnDeinit (const int reason) should be called. Between calls of these two functions, calls to all other functions of the library are possible. When developing advisors and indicators, it is also recommended to call the ATcl_OnEvent () ATcl_OnTimer () function ATcl_OnTick () within the corresponding MQL functions.

All functions of Tcl are accessible through interpreters, which are represented by the ATcl class. You can create multiple interpreters, not necessarily that it is one, the main thing is that all calls are between ATcl_OnInit and ATcl_OnDeinit.

#include <ATcl.mqh>
void OnStart() {
  ATcl_Init();			/// initialize ATcl
  ATcl *tcl= new ATcl;		/// create interp.
  Print(tcl.StringEval("info tclversion"));	/// show tcl version
  delete tcl;			/// remove interp.
  ATcl_Deinit();		/// finalize ATcl
}

Of course, before you use this you need Install All :-) that is, put Tcl in the system and place ATcl.mqh, ATcl.dll along the necessary paths.

In order to use it it was easy and simple, the StringEval, DoubleEval, IntEval methods were implemented, which execute a script and return the required value by logic. All these (and, optionally, others) are a wrapper over the general Eval (script, flags) method that executes the given script and returns the result code ( TCL_OK or otherwise.

If you rewrite an example with maximum control, it will be:

#include <ATcl.mqh>
void OnStart() {
  /// initialize ATcl
  if (ATcl_Init()!=INIT_SUCCESUL) {
    // any error in init.
    Alert("Error in ATcl_Init()");
    return;
  }
  /// create interp
  ATcl *tcl= new ATcl;
  if (tcl == NULL || !tcl.Ready()) {
    // error when creating
    Alert("Error in 'new ATcl'");
    return
  }
  /// eval script "info tcl_version"
  if (tcl.Eval("info tcl_version")!=TCL_OK) {
    // error in evaluate
    Alert(StringFormat("Error in script: %s",tcl.StringResult()));
  } else {
    // command done, and have result
    Print("Tcl version=%s",tcl.StringResult());
  }
  /// final
  delete tcl;			/// remove interp
  ATcl_Deinit();		/// finalize ATcl
}

In general, it's quite easy to work with the library, XxxEval methods execute Tcl scripts, Typename (obj) methods convert Tcl objects to Mql values, Obj (x) methods create Tcl objects from values. In addition, the Set, Get methods allow you to set and change the values of the Tcl variables.

For a more serious demonstration, there will be a script that stores quotes in the database. Take the built-in base sqlite3, although you can also have others (mysql, postgress, jdbc, monetdb are actively supported, you can also connect ODBC). Oracle has its own interface represented by a separate package oratcl.

//+------------------------------------------------------------------+
//|                                                  atcl_sqlite.mq4 |
//|                                                Maxim A.Kuznetsov |
//|                                                      luxtrade.tk |
//+------------------------------------------------------------------+
#property copyright "Maxim A.Kuznetsov"
#property link      "luxtrade.tk"
#property version   "1.00"
#property strict
 
#include "ATcl.mqh"
 
const string Shema = 
"CREATE TABLE IF NOT EXISTS Bar ("
"   symbol TEXT,"
"   period TEXT,"
"   time INTEGER NOT NULL,"
"   open REAL NOT NULL, "
"   high REAL NOT NULL, "
"   low REAL NOT NULL,  "
"   close REAL NOT NULL,"
"   volume INTEGER NOT NULL, "
"   PRIMARY KEY(symbol,period,time)"
" )";
void OnStart()
{
   ATcl_OnInit(); // enable ATcl
 
   ATcl *tcl=new ATcl; // create interp
   if (tcl==NULL || !tcl.Ready()) {
      Alert("error on new ATcl");
      ATcl_OnDeinit();
      return;
   }
   int ok=false;
   do { 
      Print("Using embedded SQLite");
      if (tcl.Eval("package require tdbc::sqlite3")!=TCL_OK) break;
      Print("Calculate path to database");
      tcl.Set("dbname",tcl.Obj("bar.db3"));
      tcl.Set("datadir",tcl.Obj(TerminalInfoString(TERMINAL_DATA_PATH)));
      if (tcl.Eval("set fullPath [ file join $datadir MQL4 Files $dbname ]")!=TCL_OK) break;
      PrintFormat("Open database базу %s",tcl.String(tcl.Get("fullPath")));
      if (tcl.Eval("tdbc::sqlite3::connection create db $fullPath")!=TCL_OK) break;
      Print("Install DB shema");
      if (tcl.Eval(StringFormat("db allrows {%s}",Shema))!=TCL_OK) break;
      Print("Prepare statement");
      if (tcl.Eval("set stmt [ db prepare {REPLACE INTO Bar (symbol,period,time,open,high,low,close,volume) VALUES (:symbol,:period,:t,:o,:h,:l,:c,:v)} ]")!=TCL_OK) break;
      Print("Create Tcl variables");
      tcl.Set("symbol",tcl.Obj(Symbol()));
      tcl.Set("period",tcl.Obj(EnumToString((ENUM_TIMEFRAMES)Period())) );
      tcl.Set("time",tcl.Obj(Time));
      tcl.Set("open",tcl.Obj(Open));
      tcl.Set("high",tcl.Obj(High));
      tcl.Set("low",tcl.Obj(Low));
      tcl.Set("close",tcl.Obj(Close));
      tcl.Set("volume",tcl.Obj(Volume));
      tcl.Set("n",tcl.Obj((long)0));
      Print("Run statement over arrays");
 
      Tcl_Obj f=tcl.Obj("foreach t $time o $open h $high l $low c $close v $volume { $stmt execute ; incr n ; if {$n==100} break }");
      tcl.Ref(f);
      if (tcl.Eval(f)!=TCL_OK) break;
      tcl.Unref(f);
      Print("remove statemnt");
      tcl.Eval("$stmt close");
      Print("close database");
      tcl.Eval("$db close");
      ok=true;
   }while(false);
   if (!ok) {
      PrintFormat("Error: %s",tcl.StringResult());
   }
   delete tcl;          // remove interp
   ATcl_OnDeinit();  // finalize ATcl
}

Execution of Tcl scripts in the interpreter. If Tcl_Obj is passed as the first parameter, it will be precompiled first.

   int Eval(Tcl_Obj, int flags=0); /// execute the Tcl script and get the success code. The result will be available via Result
   int Eval(string &,int flags=0);
   double DoubleEval(Tcl_Obj,int flags)=0; /// execute the script and immediately get the result. If an error occurs, 0.0
   double DoubleEval(string &,int flags=0);
   string StringEval(Tcl_Obj,int flags=0); /// execute the script and immediately get the result. The error will be ""
   string StringEval(string &,int flags=0);
   int Call(Tcl_Obj command,...);	// execute a command with arguments Tcl_Obj (up to 10 pieces)
   int Call(Tcl_Obj &objv[],int objc,int flags); // execute a command with arguments passed through an array

The main method is Eval - execute the script specified in the object and return the result code. The result itself will be accessible through Result. TCL_OK codes can be returned - if everything is correct, TCL_ERROR - in case of an error, TCL_BREAK, TCL_CONINUE in special cases

Typical Use-case :

   ATcl_Init();	// Initialize Tcl
   ATcl *tcl=new ATcl;	// Create interp
   ....
   Tcl_Obj script=tcl.Obj("clock seconds"); // create object
   tcl.Ref(script);	// Increase the object reference count so that it is not destroyed after the first use
	// execute the script
   if (tcl.Eval(script)!=TCL_OK) {
   	// some mistake, give Alert
        Alert(StringFormat("Error in Eval():%s",tcl.StringResult()) );
   } else {
        // there are no errors in the execution, you can use the result
        // (now script compiled and executed)
        PrintFormat("UNIXTIME: %d",tcl.LongResult());
   }
   ....
   tcl.Unref(script); // the objects to which Ref () was previously applied must be Unref () to be deleted
   ....
   delete tcl;	  // remove interp
   ATcl_Deinit(); // finalize ATcl

In order not to write such long «sheets» simple methods for executing scripts are added, for example DoubleEval - execute the script and get the result as double. And the script can be passed as an object, or an ordinary string.

   PrintFormat("sqrt(2)=%f",tcl.DoubleEval("expr sqrt(2)"));

Result methods are used to get the last result.

   Tcl_Obj Result(int code=0); 		// get the result as an object
   double DoubleResult(int code=0);	// get result in the form of double
   string StringResult(int code=0);     // get the result as string

The code argument is ignored and serves to allow «nesting» inside the Eval call:

   if (tcl.Eval("array get tcl_platform")!=TCL_OK) {
   	// some mistake, we will get a description of it
        Alert(StringFormat("Error : %s",tcl.StringResult());
   } else {
        // There are no errors, we print the information about the platform
        Print(tcl.StringResult());
   }
   // use "nesting"
   PrintFormat("Version: %f",tcl.DoubleResult(tcl.Eval("set tcl_version")) );

The Obj () methods are used to convert Mql data to Tcl objects. Create a new object with the 0th reference counter. That is, the created object will be automatically deleted after the first use. For repeated use, the method Ref (obj) is used, which this counter will enlarge. To free an object, resp. Unref (obj).

   /// creating objects
   Tcl_Obj Obj();	// create empty object
   Tcl_Obj Obj(string);	// create object from string
   Tcl_Obj Obj(double); // from double
   Tcl_Obj Obj(long);	// from long
   Tcl_Obj Obj(datetime);// from datetime
   /// ref.counts
   Tcl_Obj Ref(Tcl_Obj obj); // Increase the counter and return the same object
   Tcl_Obj Unref(Tcl_Obj obj); // reduce the reference count and return the object; 0 if the counter has been reset and the object has been deleted

The String, Double, Long methods are used to convert Tcl objects to the corresponding Mql values.

   string String(Tcl_Obj);  		// get string from object
   string String(Tcl_Obj,int index); 	// get string from list item by index
   double Double(Tcl_Obj);  		// get double from object
   double Double(Tcl_Obj,int index); 	// get double from list item by index
   long   Long(Tcl_Obj);                // similarly - getting Long
   long   Long(Tcl_Obj,int index);
   datetime Datetime(Tcl_Obj);          // the same but datetime
   datetime Datetime(Tcl_Obj,index);

When accessing the list by index, the list items are numbered from 0 (0 is the very first). Negative Indes will address elements from the end of the list (-1 is the last one).

For example:

   Tcl_Obj list=tcl.Obj("alpha beta gamma theta");
   tcl.Ref(list);
   PrintFormat("List elements: first=%s last=%s",tcl.String(list,0),tcl.String(list,-1));
   tcl.Unref(list);

A small interface to the Tcl lists.

    int ListLength(Tcl_Obj); 			// get the number of list items
    int Count(Tcl_Obj);				// a synonym for ListLength, for C #
    Tcl_Obj ListIndex(Tcl_Obj,int index);	// get list item by index
    int ListAppend(Tcl_Obj list,Tcl_Obj element); // add an item to the end of the list
    int Append(Tcl_Obj list,Tcl_Obj element);   // synonym for ListAppend

Elements of lists are numbered from 0 (0 is the very first element). If you specify a negative index, the items are selected from the end of the list (-1 is the most recent)

To create objects from arrays, use the advanced method Obj. To retrieve data from objects in arrays, the ToArray method. The syntax of ToArray is close to CopyArray

    Tcl_Obj Obj(double &array[],int pos=0,int count=WHOLE_ARRAY);// create an object from a double array
    Tcl_Obj Obj(string &array[],int pos=0,int count=WHOLE_ARRAY);// create an object from a string array
    Tcl_Obj Obj(long &array[],int pos=0,int count=WHOLE_ARRAY);// create an object from a long array
    Tcl_Obj Obj(datetime &array[],int pos=0,int count=WHOLE_ARRAY);// create an object from a datetime array
    //int ToArray(double &dst[],Tcl_Obj src,int dst_pos=0,int src_pos=0,int count=WHOLE_ARRAY);
    //int ToArray(string &dst[],Tcl_Obj src,int dst_pos=0,int src_pos=0,int count=WHOLE_ARRAY);
    //int ToArray(long &dst[],Tcl_Obj src,int dst_pos=0,int src_pos=0,int count=WHOLE_ARRAY);
    // ToArray() unimplementd jet, but not necessary

Although variable control is possible via Eval, for convenience and speed, the methods Set, Get, Unset

   Tcl_Obj Set(Tcl_Obj name,Tcl_Obj value);	// set the value of variable name
   Tcl_Obj Set(Tcl_Obj hash,Tcl_Obj key, Tcl_Obj value); // set the value of the hash element
   Tcl_Obj Get(Tcl_Obj name);			// get the value of the variable
   Tcl_Obj Get(Tcl_Obj hash,Tcl_Obj key);	// get the value of the hash element
   bool IsSet(Tcl_Obj name);	// check for variable is exists

For recommendations on the practical use of ATcl, see the Practice page

Installation instructions for ATcl and the latest version can be found on the Install

FIXME Эта страница пока что не переведена полностью. Пожалуйста, помогите завершить перевод.
(Сотрите это сообщение по окончании перевода.)