AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Compile/Run C programs from AHK w/o temp files
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Fri Apr 27, 2007 11:09 pm    Post subject: Compile/Run C programs from AHK w/o temp files Reply with quote

There are already scripts posted to the Forum, which pass a program stored in an AHK string to a compiler/interpreter, and retrieve the result. E.g., for Lua the script here works fine, but it uses temporary files, which can cause problems with name conflicts, write permissions, etc. To avoid this problem, we need a compiler, which takes its input from memory, and writes its output also to memory.

The smallest C compiler of this kind I know of is tcc, Fabrice Bellard's Tiny C Compiler. For the easiest use it has to be compiled to a Windows dll, which needs some tweaking, described by Adel Amro. You can also download the version I used from here. Just unzip it to a directory of your choice. I used c:\tcclib. It has two subdirectories, the original "include" and "lib" of tcc, with the compiled libtcc.dll and libtcc.def added. All together about 800KB. (If you install tcc, you only need to copy these two files to its "lib" subdirectory, all others are already there.)

We interface AHK to this dll, with appropriate dllcall's. There are not too many of them:

"LoadLibrary" to keep the dll in memory while needed
"tcc_new" to create a C compiler context (allocate memory, establish internal data structures)
"tcc_add_sysinclude_path" to tell it where its include (header) files are
"tcc_add_library_path" to tell it where the library is
"tcc_compile_string" to compile a program stored as a string
"tcc_run" to run it
"tcc_delete" to remove the compiler context
"FreeLibrary" to unload the dll from memory.

We have to tell the C program, where in memory to write its result. For that we allocate a large enough AHK variable, and write its <address> directly to the C source code as
char* _result = <address>;
The C program has to write its result as a string to the place _result points to, which is our AHK variable, directly available after the program terminates.

The usage is simple:
1. Assign the C source code to an AHK variable, as string. Make sure that all % are escaped with `, there is no ; or ) in the first position in a continuation line.
2. You can have references to AHK variables between % signs (%i%)
3. Include a declaration in main(): "char* _result;". The target of this pointer has to be updated with the result we want to see from the C program.
4. Run the C program with the Run function below. Its parameters are: the path to the directory containing libtcc.dll, the AHK string containing the C program and optionally an upper bound on the length of the returned AHK string:
Code:
#NoEnv
MsgBox % Run("c:\tcclib",p:="main(){char*_result;strcpy(_result,""Hello World"");}")

i = 101
C_prog =       ; floating point support
(
#include <math.h>
double f(double x) { return sqrt(x); }
main() {
  char* _result; // needed to export the result
  int i = %i%;   // AHK varible reference %i%
  sprintf(_result,"f(`%u) --> `%0.17g",i,f(i)); } // Escape `%
)
MsgBox % Run("c:\tcclib",C_Prog)

h = 0x12345678
PROG =         ; assembler support
(
typedef unsigned long UInt32;

UInt32 BSWAP(UInt32 x) { // Byte reversal
    __asm__("bswap `%0" : "=r" (x) : "0" (x));
    return x; }

main() {
  char* _result;
  sprintf(_result,"ByteSwap of `%x = `%x",%h%,BSWAP(%h%)); }
)
MsgBox % Run("c:\tcclib",PROG)


Run(libpath, ByRef prog, ResultLen=999) {
   If (0 = hModule := DllCall("LoadLibrary",Str,libpath . "\lib\libtcc.dll")) {
      MsgBox Cannot load libray %libpath%\lib\libtcc.dll
      Return
   }
   Context := DllCall("libtcc\tcc_new", "cdecl UInt")
   DllCall("libtcc\tcc_add_sysinclude_path", UInt,Context, Str,libpath . "\include", "cdecl UInt")
   DllCall("libtcc\tcc_add_library_path", UInt,Context, Str,libpath . "\lib", "cdecl UInt")

   VarSetCapacity(x,ResultLen)
   prog := RegExReplace(prog,"(char\s*\*\s*_result)([^;]*)", "$1 = " . &x, Count)
   If (Count = 0)
      MsgBox Found NO char* _result...;
   Else {
      If (DllCall("libtcc\tcc_compile_string", UInt,Context, Str,prog, "cdecl UInt") || ErrorLevel)
         MsgBox Compile error
      Else DllCall("libtcc\tcc_run", UInt,Context, "cdecl UInt")
   }
   DllCall("libtcc\tcc_delete", UInt,Context, "cdecl")
   DllCall("FreeLibrary", UInt, hModule)
   VarSetCapacity(x,-1) ; set correct length
   Return x
}

The tcclib.zip file contains a TEST.ahk file in the topmost level (similar to the script above). Unzip it anywhere, with maintaining the relative paths of files. Double clicking on TEST.ahk (or running it otherwise) should pop up a Hello World message box, and one with
f(101) --> 10.04987562112089
testing sprintf and the floating point library. When everything works, you can delete this TEST.ahk, or keep it for a quick reference.

Edit 20070429: Added #NoEnv, to avoid possible conflicts with environment variables.
Edit 20070429: Added TEST.ahk to tcclib.zip.
Edit 20070429: Fixed bug: added VarSetCapacity(x,-1) before Return. #NoEnv is not normally needed any more, but safer to keep it.


Last edited by Laszlo on Mon Apr 30, 2007 2:33 am; edited 4 times in total
Back to top
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10467

PostPosted: Sat Apr 28, 2007 9:14 am    Post subject: Reply with quote

Really amazing; great resource! Thanks for putting it all together and explaining it so clearly.
Back to top
View user's profile Send private message Send e-mail
majkinetor



Joined: 24 May 2006
Posts: 3615
Location: Belgrade

PostPosted: Sat Apr 28, 2007 10:30 am    Post subject: Reply with quote

Absolutely fantastic.
I will go to try this imediately Very Happy

If #PP directive is ever added I will make real we have something like asm directive in C. So we could write:

Code:
 ahk_code...
 ...
 ...
 tcc
 {
    sprintf(...)   
 }

without trouble of calling dll, escaping % etc...
_________________
Back to top
View user's profile Send private message MSN Messenger
Sean



Joined: 12 Feb 2007
Posts: 1282

PostPosted: Sat Apr 28, 2007 12:17 pm    Post subject: Reply with quote

Looks great.
It's too bad that I don't know C.
Back to top
View user's profile Send private message
corrupt



Joined: 29 Dec 2004
Posts: 2391

PostPosted: Sat Apr 28, 2007 7:24 pm    Post subject: Reply with quote

Interesting. Thanks Smile
Back to top
View user's profile Send private message Visit poster's website
majkinetor



Joined: 24 May 2006
Posts: 3615
Location: Belgrade

PostPosted: Sat Apr 28, 2007 9:27 pm    Post subject: Reply with quote

Hm... I get empty msg box .
I extracted archive in C: and run your script. Empty msgbox.
_________________
Back to top
View user's profile Send private message MSN Messenger
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sun Apr 29, 2007 12:46 am    Post subject: Reply with quote

Please verify if the c:\tcclib directory contains 2 subdirectories, inc and lib. (Unzip has to be performed, so the paths of files are re-created.) If all is fine, add before the Return x command:
Code:
MsgBox % "address = " . &x . "`n`nProgram =`n" . prog
The address value has to be the same as the address in the program text " char* _result = ...

If that is OK, too, another problem could be a missing or wrong C runtime library (in my PC it is C:\WINDOWS\system32\msvcrt.dll).
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sun Apr 29, 2007 3:28 am    Post subject: Reply with quote

If there are no error messages, most likely the sprintf function is not found. If the runtime C library is there, you could try to explicitly include the relevant header file: #include <stdio.h>.

The simplest C program should work, too:
Code:
MsgBox % Run("c:\tcclib",p:="main(){char*_result;strcpy(_result,""Hello World"");}")
If not, you could try adding #include <string.h>
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3615
Location: Belgrade

PostPosted: Sun Apr 29, 2007 10:53 am    Post subject: Reply with quote

No changes.

Ok, this is what I did

1. Example tcc\tcc -Iinclude -Llib examples/fib.c works. It produces working exe.

2. I downloaded your archive and extracted it to C so I have the same structure as you.

3. I saved your example in c:\tclib and executed it. I was changing example to something easy like "hello word" sprintf but nothing worked.

4. I downed latest AHK, didn't help

5. I checked program you are sending after regExp. The _result line is correct, memory is the same. Furhtermore I checked variable with HexView. This is the code I was using:

Quote:
x := "o my god"
x := Run("c:\tcclib",p:="#include <string.h>; main(){char*_result;strcpy(_result,""Hello World"");}")

HexView(x)

This is what I got
Code:
00 68 20 6D   79 20 67 6F   64 00 00 00   | .h my god...


This code works from command line
Code:
main(){char s[128]; sprintf(s, "Hello World");printf(s);}


So, I don't know what happened....
ALso, when I make syntax error, I got "compile error", when I fix it I don't, so it seems like this part is working correctly.

Can you make me standalone thing that can be extracted and runned ?
So, if it is possible for you, use relative names in includes. I will run all examples from the root of the project so my ahk script will be executed from tcclib folder. So the bottom line is that you send me script, I extract it and click ahk script without having to tweak anything.

I tryed this and it seems to work like that:
Code:
Run(A_ScriptDir, ...)


I checked on 2 different computers with different service packs, and the same thing happens.
_________________
Back to top
View user's profile Send private message MSN Messenger
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sun Apr 29, 2007 4:36 pm    Post subject: Reply with quote

Add #NoEnv in front of the script. Maybe, some environment variables conflict with internal names.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sun Apr 29, 2007 4:53 pm    Post subject: Reply with quote

I updated the tcclib.zip file. It contains now a TEST.ahk file in the topmost level. Unzip it anywhere, with maintaining the relative paths of files. Double clicking on TEST.ahk (or running it otherwise) should pop up a Hello World message box, and one with
f(101) --> 10.04987562112089
testing sprintf and the floating point library.
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3615
Location: Belgrade

PostPosted: Sun Apr 29, 2007 5:17 pm    Post subject: Reply with quote

Quote:
Add #NoEnv in front of the script. Maybe, some environment variables conflict with internal names.

ROFL Very Happy

It was that. I don't see why though... I checked what variable could do that and I don't see anything.

I really don't know what may cause this. This is the list of my EnvVars:

Code:
_NT_SYMBOL_PATH   srv*DownstreamStore*http://msdl.microsoft.com/download/symbols
ALLUSERSPROFILE   C:\Documents and Settings\All Users
APPDATA   C:\Documents and Settings\neutrino\Application Data
AUDIOROOT   D:\audio
COMMANDER_DRIVE   D:
COMMANDER_INI   D:\Utils\Total Commander\wincmd.ini
COMMANDER_PATH   D:\Utils\Total Commander
CommonProgramFiles   C:\Program Files\Common Files
COMPUTERNAME   NEUTRINO
ComSpec   C:\WINDOWS\system32\cmd.exe
CSOUND_HOME   C:\audio\--- sound hack\Csound\_tools\silence\bin\bin
INCLUDE   C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\include\
JAVA_HOME   C:\Program Files\Java\jre1.5.0_01
LIB   C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Lib\
LOGONSERVER   \\NEUTRINO
NUMBER_OF_PROCESSORS   1
OS   Windows_NT
Path   C:\Perl\site\bin;C:\Perl\bin;C:\Program Files\Windows Resource Kits\Tools\;C:\Program Files\Borland\Delphi7\Bin;C:\Program Files\Borland\Delphi7\Projects\Bpl\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;D:\Utils\_Console;D:\Utils\_Console\Rkit;D:\Utils\_Console\SysInternals;D:\Utils\_Console\SysInternals\PSTools;D:\Utils\_Scripts\Batch;D:\Utils\_Tools;C:\Program Files\Common Files\iZotope\Runtimes;C:\Program Files\Microsoft Visual Studio\Common\Tools\WinNT;C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin;C:\Program Files\Microsoft Visual Studio\Common\Tools;C:\Program Files\Microsoft Visual Studio\VC98\bin;c:\perl\bin
PATHEXT   .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
PROCESSOR_ARCHITECTURE   x86
PROCESSOR_IDENTIFIER   x86 Family 15 Model 3 Stepping 4, GenuineIntel
PROCESSOR_LEVEL   15
PROCESSOR_REVISION   0304
ProgramFiles   C:\Program Files
PYTHONPATH   ;C:\audio\--- sound hack\Csound\csound 5
SILENCE_HOME   C:\audio\--- sound hack\Csound\_tools\silence\bin
SSDIR   D:\Work\Code\CSound\SSDIR
STK_HOME   C:\audio\--- sound hack\Csound\_tools\silence\bin
SystemDrive   C:
SystemRoot   C:\WINDOWS
TEMP   C:\WINDOWS\TEMP
TMP   C:\WINDOWS\TEMP
USERPROFILE   C:\Documents and Settings\neutrino
UTILS   D:\Utils
VS71COMNTOOLS   C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\
windir   C:\WINDOWS


The funny thing is that I tried on different computer that has only standard env vars as system was recently reinstalled.

I just tried your new version and it works fine.
_________________
Back to top
View user's profile Send private message MSN Messenger
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sun Apr 29, 2007 5:40 pm    Post subject: Reply with quote

You are right, it is funny. I filed a bug report.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sun Apr 29, 2007 6:26 pm    Post subject: Reply with quote

If you embed a longer C program, and it has errors, a "Compile error" message is not very helpful. For more information, there is a callback mechanism in libtcc:
Code:
/* set error/warning display callback */
void tcc_set_error_func(TCCState *s, void *error_opaque,
                        void (*error_func)(void *opaque, const char *msg));
Ideally, this callback function would be a MsgBox, showing the error found.

Has anyone succeeded with using AHK functions in callback?

There is an ugly workaround: at a compile error we can write the program into a temp.c and run the standalone tcc compiler, which shows its error messages. After reading it, the script can delete the temporary file. The main point of this post was, however, to avoid temporary files, and although error handling is not the normal operation, it was nicer to do the callback. Any ideas?
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3615
Location: Belgrade

PostPosted: Sun Apr 29, 2007 7:41 pm    Post subject: Reply with quote

Quote:
If you embed a longer C program,

I would make it to work as normal c code first , without AHK in that case. Pretty much the same as your "ugly workaround" but manuely.

Quote:
Has anyone succeeded with using AHK functions in callback?

One of those things that are not possible to do in AHK. Its pitty Chris don't want to use C/Invoke in AHK which is used in Lua, Java and some other languages for FFI and it has very nice, dllcall, struct and callback encapsulation.

So, no ideas for this case, except if Chris support callback which is IMO very needed as it will allow hooks that can be used to automate almost everything in the system.
_________________
Back to top
View user's profile Send private message MSN Messenger
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group