obsługa stringów z dll do MQL

O jezykach programowania w platformach i nie tylko.
259
Maniak
Maniak
Posty: 3968
Rejestracja: 15 cze 2011, 23:20

Nieprzeczytany post autor: 259 »

Esco pisze:
259 pisze: Np. wystarczy przekazać Close[] jako parametr (dla ułatwienia wejściowy i wszystko jedno tablicę czy wskaźnik do tej tablicy) do dll żeby wyparować całość przy pierwszym dotknięciu. Kto wie dlaczego? Wink
Close pewnie jest w pamięci chronionej głównego wątku MT4.
Tak... tutaj dotykasz tego co się wielu wymyka... ale... co to jest Close[]?
Już nie będę męczył - to jest tablica zarządzana dynamicznie przez MQL Nie tylko jeżeli chodzi o jej stworzenie i modyfikację zawartości ale również jej rozmiar. I to jest w tym przypadku najistotniejsze: zmiana rozmiaru tablicy aby pomieścić nowe dane jak przychodzą od brokera.
Podaj wskaźnik takiej tablicy zewnętrznemu modułowi podczas gdy MQL zmienia ją sobie jak przychodzą tiki (realloc) i w momencie gdy ten moduł usiłuje właśnie ją czytać "myśląc" sobie, że to const... BAM! To jest tylko kwestia czasu...

Dodano po 20 minutach:

Tak Esco - to jest brak możliwości synchronizacji pomiędzy... i tu mam problem bo teoretycznie to jest ten sam wątek, ale zmienia się wskaźnik istotnej tablicy i to nie jest synchronizowane pomiędzy dwoma istotnymi fragmentami kodu.
Jakże często ludzie mają już gotową opinię zanim zdążą pojąć istotę rzeczy.
A gdy już ta istota w pełni do nich dotrze, jakże często muszą zmagać się z konsekwencjami swojej opinii ;-)

crn
Gaduła
Gaduła
Posty: 117
Rejestracja: 17 mar 2009, 22:07

Nieprzeczytany post autor: crn »

wlasnie to mialem an mysli :)

tam ejscze jakeis inne cyrki sa. ja przekazywalem symoble np EURUSD to otryzmywalem np. EURUSDEURU w zaleznosci od wielkosci tablicy.

powinno pomoc jak na sztywno wpsizesz string example =" sdsaddfsdfsdfsdfsdfsdfsdfsdfsdfsdfsd";

tylko poamietaj zeby podmienic to wtedy na taka sama ilsoc znakow
259 pisze:
Esco pisze:
259 pisze: Np. wystarczy przekazać Close[] jako parametr (dla ułatwienia wejściowy i wszystko jedno tablicę czy wskaźnik do tej tablicy) do dll żeby wyparować całość przy pierwszym dotknięciu. Kto wie dlaczego? Wink
Close pewnie jest w pamięci chronionej głównego wątku MT4.
Tak... tutaj dotykasz tego co się wielu wymyka... ale... co to jest Close[]?
Już nie będę męczył - to jest tablica zarządzana dynamicznie przez MQL Nie tylko jeżeli chodzi o jej stworzenie i modyfikację zawartości ale również jej rozmiar. I to jest w tym przypadku najistotniejsze: zmiana rozmiaru tablicy aby pomieścić nowe dane jak przychodzą od brokera.
Podaj wskaźnik takiej tablicy zewnętrznemu modułowi podczas gdy MQL zmienia ją sobie jak przychodzą tiki (realloc) i w momencie gdy ten moduł usiłuje właśnie ją czytać "myśląc" sobie, że to const... BAM! To jest tylko kwestia czasu...

Dodano po 20 minutach:

Tak Esco - to jest brak możliwości synchronizacji pomiędzy... i tu mam problem bo teoretycznie to jest ten sam wątek, ale zmienia się wskaźnik istotnej tablicy i to nie jest synchronizowane pomiędzy dwoma istotnymi fragmentami kodu.

259
Maniak
Maniak
Posty: 3968
Rejestracja: 15 cze 2011, 23:20

Nieprzeczytany post autor: 259 »

Z tym że to opisujesz wygląda mi na obejście nieprawidłowego przekazywania danych. EDIT: albo wcale nie - jest prawidłowym obejściem czegoś innego - zobacz niżej komentarz po przykładowym kodzie dll.
Zasada jest taka, że dane wejściowe należy najpierw skopiować przed dalszą obróbką w dll, a wyniki przekopiować na wyjście. A na końcu posprzątać po sobie czyli usunąć wszelkie alokacje które trzeba było zrobić w kodzie dll bo inaczej będą narastać śmieci.
I wtedy nie będzie to miało znaczenia co jest w tym stringu który ma być wyjściem, a nie wejściem.
Natomiast ma znaczenie w jakiej formie są przekazywane te dane.
Co innego string, co innego string[] i na dodatek w samym dll tez są przynajmniej dwie możliwości wyjścia, każda wymaga innego podejścia.

Ja wybrałem dane jednoelementowe czyli chcę mieć w MQL jeden string z kilku innych. Ale w przypadku tabelek stringów sprawa jest trudniejsza, bo to już jest struktura którą trzeba sobie odtworzyć w dll. I tutaj zaczynają się schody bo Metaquotes nie opisuje tej struktury… przynajmniej nie wprost ;-)

Tak samo jak nie opisuje jak alokowany jest string. To, że MQL alokuje automatycznie 64k dla tego co wychodzi z funkcji dll znalazłem grzebiąc rozpaczliwe po rożnych dziwnych miejscach.
Ale 64k to nie jest limit. Właśnie przed chwilą zrobiłem sobie string ponad 400MB, łącząc krótsze przez StringConcatenate() w pętelce.
W samym MQL są rozmaite dziwne rzeczy. Np piszą, że string constant nie może być dłuższy niż 255 znaków. To jest string constant:

Kod: Zaznacz cały

string test = "1234567890";
Ale ten sam test można teraz użyć jako zmienną a nie stałą:

Kod: Zaznacz cały

for (int i=0;i<50000;i++) test = StringConcatenate(test,"1234567890");
I teraz może on być znacznie dłuższy. Uwaga, wykonanie tej pętli trochę trwa, podejrzewam że większość tego czasu jest potrzebna na zarządzanie pamięcią ;-)

Dalej, piszą o string constant:
Its internal representation is a structure of 8 bytes. The first element of the structure is a long integer that contains the size of the buffer distributed for the line. The second element of the structure is 32-order address of the buffer that contains the line.
Ale w takim razie skoro można przekazać ten string wprost choć przecież funkcja w dll odbiera to jako char* to oznacza, że MQL musi niejawnie podawć "The second element" wskaźnik do pierwszego znaku stringu. Bo to co dostaje dll jak byk jest c stringiem: ciągiem znaków zakończonych NULL.

A teraz, skoro wewnętrzna struktura stringu MQL jest dwuelementowa: {długość;wskaźnik} to jak będzie wyglądać tablica stringów w MQL, a jak będzie to wyglądać jak przekaże się taką tablicę do dll?

Dodano po 37 minutach:

Zresztą można się podeprzeć przykładowym dll - to jest przykład obrabiania tablic stringów - zwróćcie uwagę jak zdefiniowana jest ta tablica, jako tabelka struktur opisujących/wskazującyh stringi, a nie tabelka samych stringów rozumianych jako ciąg znaków (w nowomowie informatycznej to jest tabelka metadanych, a nie samych danych ;-) ):

Kod: Zaznacz cały

struct MqlStr
  {
   int               len;
   char             *string;
  };

MT4_EXPFUNC int __stdcall ProcessStringArray(MqlStr *arr,const int arraysize)
  {
   int   len1,len2;
//----
   if(arr==NULL)
     {
      printf("ProcessStringArray: NULL array\n");
      return(-1);
     }
   if(arraysize<=0)
     {
      printf("ProcessStringArray: wrong arraysize (%d)\n", arraysize);
      return(-1);
     }
//----
   for(int i=0; i<arraysize-1; i++)
     {
      if(arr[i].string==NULL) len1=0;
      else len1=strlen(arr[i].string);
      if(arr[i+1].string==NULL) len2=0;
      else len2=strlen(arr[i+1].string);
      //---- uninitialized string
      if(arr[i+1].string==NULL) continue;
      //---- destination string is uninitialized and cannot be allocated within dll
      if(arr[i].string==NULL)   continue;
      //---- memory piece is less than needed and cannot be reallocated within dll
      if(arr[i].len<len1+len2)  continue;
      //---- final processing
      strcat(arr[i].string,arr[i+1].string);
     }
//----
   return(arraysize);
  }
Aha, tutaj mamy to co opisuje crn:

Kod: Zaznacz cały

      //---- destination string is uninitialized and cannot be allocated within dll
      if(arr[i].string==NULL)   continue;
Jeżeli w tablece był string który nie był jeszcze niczym wypełniony, to sprawdzając jego długość mamy NULL, a sprawdzając pierwszy znak mamy adres NULL i w efekcie go po prostu nie ma. Jest puste miejsce w tabelce, nic za nim nie stoi i pisanie tam będzie dawało wszelkie możliwe efekty oprócz zamierzonych.
Ale teraz kłóci mi się to z tym co robi MQL ze stringami i pamięcią... jasne! MQL zarządza stringami automatycznie: dynamicznie dopasowuje wielkość stringu do danych jakie maja tam trafić. Dll nie ma takiej możliwości - może sie poruszać tylko w tym co MQL mu przekazał!

I w tym świetle:

Kod: Zaznacz cały

string example = "asdsfsddsfgdfggggggggggggggggggggggggggggggggggggggg";
nabiera sensu jako obejście ograniczenia możliwości zarządzania pamięcią w dll.

Inaczej mówiąc: jak w MQL zrobię:

Kod: Zaznacz cały

string test;
to w efekcie dostaję: len=0;adres=0;

String jest ale pusty. Jak przekaże to jako bufor to dll to czego można się spodziewać jak zacznie tam coś bazgrać?

Jak napiszę:

Kod: Zaznacz cały

test = "0123456789"
to dostanę: len=10;adres=... coś tam będzie.

I będę miał buforek na 10 znaków. Jak to przekaże do dll będzie mógł tam wpisać DO 10-u znaków - to jest właśnie to o czym pisał crn :)

Uwaga - w tym kodzie arr jest zarówno wejściem jak i wyjściem: przekazywana jest tablica i ta tablica jest modyfikowana w dll. Co jest nie jest zbyt bezpieczne gdyż a) niszczy się oryginalne dane, b) nie jest wcale tak łatwo kontrolować zakres pamięci w jakim można się poruszać. W tym przykładzie niby to jest proste ale tylko dlatego, że sam przykład jest prosty.
Lepiej jednak dane wejściowe przekopiować do jakiegoś wewnętrznego bufora, a na końcu sprawdzić czy wyjście pomieści wynik przed przekopiowaniem na wyście. I rozdzielić wejście od wyjścia w definicji funkcji.
Z drugiej strony, w prostych zastosowaniach metoda z tego przykładu będzie znacznie łatwiejsza i szybsza. I nie zostawia śmieci do sprzątania.
Jakże często ludzie mają już gotową opinię zanim zdążą pojąć istotę rzeczy.
A gdy już ta istota w pełni do nich dotrze, jakże często muszą zmagać się z konsekwencjami swojej opinii ;-)

259
Maniak
Maniak
Posty: 3968
Rejestracja: 15 cze 2011, 23:20

Nieprzeczytany post autor: 259 »

Chyba rozkminiłem te stringi do końca. Wcale nie jest takie proste bo nawet w samym MQL nie jest to takie jasne, a Metaquotes nie trzyma się też własnej dokumentacji. Tak czy inaczej, zawsze trzeba wiedzieć co kryje się choćby pod takim niepozornym kodem w MQL:

Kod: Zaznacz cały

string str_arr[2] = {"jeden","dwa"};

string JEDEN = str_arr[0];
string DWA = str_arr[1];
I teraz: co się stanie jak zmienię str_arr[0] czy str_arr[1]? A co się stanie jak zmienię JEDEN czy DWA? No może to nie wystarczy... powiedzmy, że przepuszczę str_arr przez dll który trochę tam namiesza, ale adresy pamięci i powiedzmy długości stringów pozostaną bez zmian...

Jest wiele rzeczy o których trzeba pamiętać od tego jak co jest opisywane wewnętrznie przez MT4 i dll, poprzez co gdzie co jest alokowane do tego co gdzie i jak jest zwracane i na koniec jakie wywołuje to skutki. A te często bywają odmienne od oczekiwań z jednego błahego powodu: braku konsekwencji producenta oprogramowania w połączeniu z brakiem porządnej dokumentacji.
Jakże często ludzie mają już gotową opinię zanim zdążą pojąć istotę rzeczy.
A gdy już ta istota w pełni do nich dotrze, jakże często muszą zmagać się z konsekwencjami swojej opinii ;-)

Awatar użytkownika
luktom
Gaduła
Gaduła
Posty: 197
Rejestracja: 19 gru 2007, 14:39

Nieprzeczytany post autor: luktom »

Witaj,

Generalnie to co się sprawdza świetnie, nie powoduje problemów i wycieków pamięci oraz pozwala na zwracanie dowolnej długości stringa (rozpatruję przypadek zwracania pojedynczego stringa, ale na tablicach też chodzi) z DLLi to taka konstrukcja:

(MQL)

Kod: Zaznacz cały

#import "My.dll"
   void GetString(string& arr[]);
#import

string GetStringFromDll()
{
   string tmp[2] = { "a", "b" };
  
   GetString(tmp);
      
   return(tmp[0]);
}

int start()
{
   string s = GetStringFromDLL();

   Print(s);
}

(C++)

Kod: Zaznacz cały

struct MqlStr
  {
   int               len;
   char             *string;
  };

__declspec(dllexport) const void __stdcall GetString(MqlStr* value)
{
	string response="Tutaj może być dowolnej długości string otrzymany dowolną metodą";

	value->string = (char*) response.c_str();
	value->len = response.length();
}
Korzystam z tej konstrukcji codziennie, dopracowanie tego trochę mi zajęło, no ale działa bardzo dobrze :)

Zwracanie char* z DLLi mimo, że pozornie na początku działa, to jednak w dłuższej perspektywie powoduje różne dziwne anomalie więc nie polecam.

Bez problemu natomiast działa przekazywanie char* w parametrach do DLL, pod warunkiem, że DLL nie modyfikuje w żaden sposób tak przekazanych danych.

Pozdrawiam

luktom :: Łukasz Tomaszkiewicz
algotronic- zaawansowane rozwiązania dla traderów
Odwiedź naszą stronę WWW, aby poznać pełną ofertę

ODPOWIEDZ