   Implementing  type-free data in C++ (maybe assembly?)
  I guess the 1st question that might come to mind is why am I doing this?! Well my intro game is on pause because I have a feeling this can get me into the black sooner. I'm essentially writing as part of a larger project an MQL4 emulator. For those who don't know what MQL4 is, it's a language that's provided with the Metatrader trading platform that allows you to write indicators and automated trading scripts.Most of the porting has been relatively easy, as an example:#include <stdlib.h>#include <math.h>inline double MathAbs(double d) {return ::fabs(d);}inline double MathArccos(double d) {return ::acos(d);}inline double MathArcsin(double d) {return ::asin(d);}inline double MathArctan(double d) {return ::atan(d);}inline double MathCeil(double d) {return ::ceil(d);}inline double MathCos(double d) {return ::cos(d);}inline double MathExp(double d) {return ::exp(d);}inline double MathFloor(double d) {return ::floor(d);}inline double MathLog(double d) {return ::log(d);}//......For the most part, as I was doing this, it looks like it can pretty much sit on top of Win32/C++. It's just a restricted version.In the interest of getting this up and running sooner then later. I decided to implement the emulator on top of C++ (as opposed to tweaking an open source compiler which I think is above me atm).There is one function I wanted to emulate that I'm having a hard time doing.... StringConcatenate(...). It can take any number (well the metatrader manual say a max of 64) of strings, bool, double, or int. Writing a simple function I saw this in disassembly:63:  test(0,true,"Hello",50,324.234,s);0040204C   sub    esp,10h0040204F   mov    ecx,esp00402051   mov    edx,dword ptr [ebp-20h]00402054   mov    dword ptr [ecx],edx00402056   mov    eax,dword ptr [ebp-1Ch]00402059   mov    dword ptr [ecx+4],eax0040205C   mov    edx,dword ptr [ebp-18h]0040205F   mov    dword ptr [ecx+8],edx00402062   mov    eax,dword ptr [ebp-14h]00402065   mov    dword ptr [ecx+0Ch],eax;String s from here to top . Not passing s removes these lines00402068   push   407443BEh0040206D   push   76C8B439h00402072   push   32h00402074   push   offset string "Hello" (00418040)00402079   push   10040207B   push   00040207D   call   @ILT+430(test) (004011b3)00402082   add    esp,28h16:   void test(...) {}00401D50   push   ebp00401D51   mov    ebp,esp00401D53   sub    esp,40h00401D56   push   ebx00401D57   push   esi00401D58   push   edi00401D59   lea    edi,[ebp-40h]00401D5C   mov    ecx,10h00401D61   mov    eax,0CCCCCCCCh00401D66   rep stos    dword ptr [edi]00401D68   pop    edi00401D69   pop    esi00401D6A   pop    ebx00401D6B   mov    esp,ebp00401D6D   pop    ebp00401D6E   retAs a test I removed the string and added a parameter list but it shows only using offsets:16:   void test(int i=0,bool b = false, char * c= 0,int ii = 0,double d = 0.0,...) {00401D40   push   ebp00401D41   mov    ebp,esp00401D43   sub    esp,40h00401D46   push   ebx00401D47   push   esi00401D48   push   edi00401D49   lea    edi,[ebp-40h]00401D4C   mov    ecx,10h00401D51   mov    eax,0CCCCCCCCh00401D56   rep stos    dword ptr [edi]17:  i += ii;00401D58   mov    eax,dword ptr [ebp+8]00401D5B   add    eax,dword ptr [ebp+14h]00401D5E   mov    dword ptr [ebp+8],eax18:  b = false;00401D61   mov    byte ptr [ebp+0Ch],019:  ii = c[0];00401D65   mov    ecx,dword ptr [ebp+10h]00401D68   movsx  edx,byte ptr [ecx]00401D6B   mov    dword ptr [ebp+14h],edx20:  d *= d;00401D6E   fld    qword ptr [ebp+18h]00401D71   fmul   qword ptr [ebp+18h]00401D74   fstp   qword ptr [ebp+18h]21:   }00401D77   pop    edi00401D78   pop    esi00401D79   pop    ebx00401D7A   mov    esp,ebp00401D7C   pop    ebp00401D7D   retI'm trying to get a sense on how to tackle this problem. Looking at the assembly code it looks like it might be possible to get some semblence of the datatype prior to the function call if I don't use aligned memory (I think thats the term, it's been a while)... so a bool for instance will take up 1 byte and not 4....... I was thinking pushing a double is one instruction although its two :/ ..... perhaps thats not the best way to go.I'm really stumped on this one. Thats pretty much what it comes down to. :( Can this be done on top of C++ or am I stuck tweaking a open source compiler?   
  vararg functions cannot empirically determine the number or types of arguments passed to them. Functions like printf() depend on a format specifier to give information on what the rest of the arguments look like; if they don't match, the result is undefined behavior.   ;template < class P1 >std::string concat(P1 p1) {std::stringstream buf;buf << p1;return buf.str();}template < class P1, class P2 >std::string concat(P1 p1, P2 p2) {std::stringstream buf;buf << p1 << p2;return buf.str();}template < class P1, class P2, class P3 >std::string concat(P1 p1, P2 p2, P3 p3) {std::stringstream buf;buf << p1 << p2 << p3;return buf.str();}// repeat as neededconcat(17);concat(17, "Hello World");concat(17, true, 20);Obviously, the sanity of such approach remains questionable, considering that one could just use streams directly.   ;  @SneftelI know. That's why this whole thing has me stumped. I was hoping there might be some way to determine how far the stack pointer advanced with each push and comparing it to the previous value. The difference giving a clue to what type it might be..... but with doubles using two instructions I'm not really sure if that can be used at all.And correct me if I'm wrong but now that I think about it even if I wrote custom assembly code for going into and out a function.... that's after all the parameters are pushed onto the stack anyway.PHP/&#106avascript must accomplish it somehow... my brain isn't really comprehending how atm :/   ;Quote:Original post by RobioneThere is one function I wanted to emulate that I'm having a hard time doing.... StringConcatenate(...). It can take any number (well the metatrader manual say a max of 64) of strings, bool, double, or int. Writing a simple function I saw this in disassembly:...I'm really stumped on this one. Thats pretty much what it comes down to. :( Can this be done on top of C++ or am I stuck tweaking a open source compiler?You can easily write this function yourself using variable arguments. In this example function, I use a static buffer of a fixed size as the workspace to build the custom string and return it as a C++ string so you don't have to worry about memory. If you need larger strings, you will need to use dynamic memory for the workspace allocation at the start of your program and be sure to free it at the end of your program.std::string StringConcatenate(const char * format, ...){static char workspace[131072]; // Just assuming you won't need moreva_list args;va_start(args, format);vsnprintf_s(workspace, sizeof(workspace), sizeof(workspace) - 1, format, args);va_end(args);return std::string(workspace);}int main(int argc, char** argv){printf("%s\n", StringConcatenate("%i, %f, %s", 42, 3.1315, "test").c_str());return 0;}You will want to reference the printf documentation to get a list of how to use the different types.That is the most straightforward way. You can use C++ only mechanisms, but it gets a little more complicated. boost:any could be used and you could declare 64 functions, but I don't think you really want to do that. [lol] The shown method above should be workable for your task I think.   ;  @AntheusTYVM.... I haven't tried it yet but looks good. I've never done much with streams.;  @Drew_BentonUnfortunately the MQL4 declaration is: string StringConcatenate(...)So the end-user couldn't just copy/paste their code into the emulator and have it run. They'd have to add the string indicating parameter datatypes. I haven't heard of the boost element you mentioned. I'll look into that.TYVM everyone for the help. It's coming so fast I can't even type one response w/o another already added. TYVM it's much appreciated.;  If you happened to have a compiler that already supports variadic templates of C++0x...   ;  @visitorI'll have to see if the "new" MS VC++ Lite can do that. I have a fencing class so I'll bbiab.   ;Quote:Original post by Antheustemplate < class P1 >std::string concat(P1 p1) {std::stringstream buf;buf << p1;return buf.str();}...Obviously, the sanity of such approach remains questionable, considering that one could just use streams directly.For everyone's amusement since this seems to be the best way to go, here's a function to generate all that code.#include <string>#include <sstream>#include <fstream>void GenerateStringConcatenateCode(int maxVariables, const std::string & delim){std::stringstream code;code << "#include <string>\n";code << "#include <sstream>\n\n";code << "namespace Global\n{\n\n";for(int x = 1; x <= maxVariables; ++x){code << "\ttemplate < ";for(int y = 0; y < x; ++y){code << "class Type" << y + 1;if(y + 1 < x)code << ", ";}code << " >";code << "\n";code << "\tstd::string StringConcatenate(";for(int y = 0; y < x; ++y){code << "Type" << y + 1 << " var" << y + 1;if(y + 1 < x)code << ", ";}code << ")\n";code << "\t{\n";code << "\t\tstd::stringstream buf;\n";code << "\t\tbuf << ";for(int y = 0; y < x; ++y){code << "var" << y + 1;if(y + 1 < x){code << " << \"" << delim << "\" << ";}}code << ";\n";code << "\t\treturn buf.str();\n";code << "\t}\n\n";}code << "}\n";std::ofstream of("StringConcatenate.h");of << code.str();of.close();}int main(int argc, char** argv){GenerateStringConcatenateCode(64, " ");return 0;}Will generate output just like Antheus showed. #include <string>#include <sstream>namespace Global{template < class Type1 >std::string StringConcatenate(Type1 var1){std::stringstream buf;buf << var1;return buf.str();}template < class Type1, class Type2 >std::string StringConcatenate(Type1 var1, Type2 var2){std::stringstream buf;buf << var1 << " " << var2;return buf.str();}template < class Type1, class Type2, class Type3 >std::string StringConcatenate(Type1 var1, Type2 var2, Type3 var3){std::stringstream buf;buf << var1 << " " << var2 << " " << var3;return buf.str();}   ... snip ...}I went ahead and wrapped the functions in their own namespace, which you don't have to. In addition, you can specify the delimiter for the variables. Here's a simple demo program of it in use.#include "StringConcatenate.h"#include <stdio.h>int main(int argc, char** argv){printf("%s\n", Global::StringConcatenate(1, 2, 3, 'a', 'b', 'c', 1.1, 2.2, 3.3, "4a4a4", "5b5b5", "7c7c7").c_str());return 0;}And the expected output:1 2 3 a b c 1.1 2.2 3.3 4a4a4 5b5b5 7c7c7Press any key to continue . . .It's only minor work to make it return a const char * instead of a std::string if you need it in that format instead. Likewise if you need to change anything else, all of the code is automatically generated so you don't have much real work to do.Hope that works out [lol]   