I am writing a c++ code to eval a tcl proc. i have two TCL files. any of two can have the tcl proc
say file1.tcl has tcl proc mytest.
file2.tcl is sourcing file1.tcl so if i do [info procs mytest] in file2.tcl , i am able to get the tcl proc name mytest.
But in the c++ when I try to eval the proc, it says invalid command mytest.
However if I write proc mytest in file2.tcl ,it works.
file1.tcl is user input and file2.tcl is default input. only in case proc is missing in file1.tcl, I will call file2.tcl proc.
Please suggest how can i get it work.
Thanks
Ruchi
Your question isn't entirely clear, but it seems that you have two files, one of which you control (and which provides default implementations of some Tcl commands?) and the other of which is controlled by your users (so they can override things?). You then want to load these into a Tcl interpreter context so that you can call things from C++? I'll assume that's what's going on.
Firstly, from the Tcl perspective you do this by getting the context (the Tcl_Interp*) to source the file with the default implementations. (There are other ways to do it, but that's definitely easiest.) Only after that is done would you source the file with the user definitions, and you would only do the call in to kick things off after everything has finished sourcing in correctly. So that's what we're aiming for.
From C++, the main things to note are that Tcl_EvalFile() is the equivalent of source in Tcl (just as Tcl_Eval() is the equivalent of eval), and that you have to take care with checking for errors; Tcl doesn't map its exception system to C++ exceptions at all, so check those return codes.
Tcl_Interp *interp = Tcl_CreateInterp();
if (Tcl_EvalFile(interp, "file2.tcl") != TCL_OK) {
const char *errorMessage = Tcl_GetString(Tcl_GetObjResult(interp));
// Deal with error
cerr << "Problem in library: " << errorMessage << "\n";
exit(1);
}
if (Tcl_EvalFile(interp, "file1.tcl") != TCL_OK) {
const char *errorMessage = Tcl_GetString(Tcl_GetObjResult(interp));
cerr << "Problem in user code: " << errorMessage << "\n";
exit(1);
}
// Everything ready
if (Tcl_Eval(interp, "[the_party get] start") != TCL_OK) {
// Something still went wrong...
Related
I have a .cfg file and I'd like to use an environment variable to configure one of the fields.
directory=${HOME}/folder1/
However, when I parse this config, it's reading ${HOME} as a string, which is obviously not what I want.
I wrote my own parser in C++, in case I need to do something special. Right now it is a very basic read and parse.
void Config_Parser::parse_config_by_delimiter(string config, string delimiter) {
ifstream infile(config);
while (infile >> line) {
key = line.substr(0, line.find(delimiter));
value = line.substr(line.find(delimiter)+1);
if (this->config_settings.find(key) != this->config_settings.end()) {
cout << "Cannot use config... same key is set multiple times" << endl;
}
this->config_settings.insert({key, value});
}
}
The code seems to work fine for all other config settings (anything not using an environment variable), so I don't think its a problem with the code. But, I am a C++ noobie, so it's here anyways.
When I parse and print out the value:
Actual output: ${HOME}/folder1/
Expected/desired output: /home/my_dir/folder1/
Untested
You can use wordexp to do posix shell-like expansion of strings.
The function wordexp() performs a shell-like expansion of the string
s and returns the result in the structure pointed to by p.
You will need to #include <wordexp.h>
You also probably want to specify the flag WRDE_NOCMD to prevent subshell command execution.
http://man7.org/linux/man-pages/man3/wordexp.3.html
Is the following configuration syntax acceptable to you?
directory = getenv("HOME") + "/folder1/";
If so, then a configuration file parser library I wrote called Config4* can do what you want. You can find it on http://www.config4star.org.
I recommend you scroll down the web page to "Download the Manuals" and retrieve Config4* Getting Started Guide and Config4* C++ API Guide. Chapters 2 (overview of syntax) and 3 (overview of API) of the "Getting Started" guide should be more than sufficient to get you up and running.
I'm using the google logger (glog) in a C++ project, where source code contains printout of informational items and progress:
std::cout << some_useful_stuff << "\n";
And I can use GLOG to log informational items:
LOG(INFO) << some_useful_stuff << "\n"; \\ gets echoed to stdout
I can't change all the std::cout statements throughout the codebase (as I'm using some subrepos from third parties etc, and unable to add my own logging statements to them), this prevents me from using an if-else construct (which would be ugly anyway, wrapping every single output statement in logic).
So how could I replicate stdout outputs to GLog?
std::cout << some_useful_stuff << "\n"; \\ message also goes to LOG(INFO)
Possibly useful: I actually don't need this to happen at the same time; it's useful to have a choice like {CONSOLE, LOGS, BOTH} but I could put up with the simpler choice between {CONSOLE, LOGS}.
Also - experienced programmer but C++ newbie so if this is mega simple; apologies - and thanks in advance!
Not sure about glog, but usually we use dup2 system call to redirect output of one file handler to another.
For e.g. in your case to make all std::cout write to the log file used by glog just do
dup2(glog_file_handler,stdout);
This would work if you can some how get filehandle of the log file that glog is writing to. Refer http://man7.org/linux/man-pages/man2/dup.2.html for more details.
Hope this helps.
P.S. This won't work if your program has multiple threads/procesess writing to same glog file and if glog does synchronized writes to log file
I am using the SOCI library to programmatically interact with a SQL Server DB via ODBC. I am having trouble getting values via output parameters in stored procedures. Some code (modeled after SOCI documentation)...
/*
* create procedure
* Dan.soci_proc #id int, #name varchar(32) output
* as begin
* set nocount on;
* select #name = name from Dan.soci_test where id = #id
* end
*/
std::string sql = "Dan.soci_proc :id, :name";
std::string name;
int proc_ndx = 1;
soci::procedure proc = (my_soci.get_session().prepare << sql, soci::use(proc_ndx),
soci::use(name));
std::cout << "\nAttempting to execute stored procedure " << size << " times."
<< std::endl;
for (; proc_ndx < adj_size; ++proc_ndx) {
try {
proc.execute();
while (proc.fetch())
std::cout << "Fetched: " << name << std::endl;
} catch (const soci::odbc_soci_error& e) {
std::cerr << "Error executing stored procedure." << std::endl;
std::cerr << e.what() << std::endl;
std::cerr << e.odbc_error_message() << std::endl;
return;
}
}
My code does not throw any errors or exceptions, but nothing is fetched, either. I have tried calling it many different ways (normal exec syntax, ODBC call syntax, etc.), but nothing seems to work. I'm wondering if the error goes back to this from here...
If an input/output parameter is omitted, or if a literal is supplied
for the parameter, the driver discards the output value.
Unfortunately, SOCI doesn't really seem to support parameter markers, at least as far as I can tell.
I have made the code work by using this kind of syntax...
std::string sql = "declare #name varchar(32); "
"exec Dan.soci_proc :id, #name = #name output; select #name";
...
soci::procedure proc = (my_soci.get_session().prepare << sql, soci::use(proc_ndx),
soci::into(name));
But that isn't ideal, for reasons that I think are obvious.
Has anyone out there used SOCI and have some input into what I need to do differently to get this to work?
EDIT: I received the following response from the soci-users forum/mailing list...
I haven't used SOCI for a couple of years but I did successfully get
stored procedures to work with ODBC on SQL Server, end of 2011, soci
version 2 (I think).
I remember that to do it I had to use statement rather than procedure.
I had to amend the backend to call SQLMoreResults in a loop.
Output parameters I think were not supported in SOCI and I handled
them direct with ODBC over which I also had a C++ layer.
However, since I'm a recent college grad (just started my first job this month!) and relatively inexperienced with C++ and database applications, I would really appreciate as much help as people are able to offer! For instance, regarding the above response - the last two points are not exactly clear to me.
The problems is that I was using the SOCI ODBC, which apparently does not support stored procedures.
SOCI Existing Backends
SOCI ODBC Backend - Stored Procedures
In the months since I initially asked this question, I have implemented my own SOCI backend, for which stored procedures work perfectly!
When using Tcl C++ API Tcl_Eval, if it returns TCL_ERROR, the error message can be retrieved from Tcl_GetStringResult(interp). However, when executing a bunch of tcl script, the error message doesn't indicate which line the script fails.
Eg:
can't find package foobar
while executing
"package require foobar"
(file "./test.tn" line 5)
Tcl_GetStringResult(interp) doesn't provide this information: (file "./test.tn" line 5). Is there a way to print out the call stack like in tcl interpreter so that I know which line the script fails?
The information you are looking for, the error info (i.e., stack trace), is in the global errorInfo variable. This information may be retrieved with Tcl_GetVar or one of its related functions. One of the best to choose is Tcl_GetVar2Ex (the name being a product of a slowly evolving API) which is highly efficient:
Tcl_Obj *infoObj = Tcl_GetVar2Ex(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
Then you use Tcl_GetString to extract the human-readable part as a char * (treat as const).
I use the following code to pull back and report on the errorinfo that is available from the interpreter.
Tcl_Obj *top_interpInfoName ;
Tcl_Obj *top_interpInfo ;
top_interpInfoName = Tcl_NewStringObj("errorInfo", -1) ;
Tcl_IncrRefCount(top_interpInfoName) ;
top_interpInfo = Tcl_ObjGetVar2(tcl_interp,
top_interpInfoName,
NULL,
TCL_LEAVE_ERR_MSG) ;
Tcl_IncrRefCount(top_interpInfo) ;
ERR_REP2("ERROR: %s", Tcl_GetString(top_interpInfo)) ;
Tcl_DecrRefCount(top_interpInfoName) ;
Tcl_DecrRefCount(top_interpInfo) ;
ERR_REP2 is my error reporting macro you need to replace it with your own.
I am looking for a good approach to build a C++ GUI application. Its core is generating whole bunch of tcl code and execute it through Tcl C++ API (#include <tcl.h>). The GUI provides an easy way for users to complete those tcl scripting tasks.
In other words, inside each callback function associated with the GUI control, it's like using ostringstream to populate tcl code and passes it to tcl interpreter. Eg:
bool execTclProc1(options) {
ostringstream oss;
oss << "package require MyPackage \n";
string optionsForTcl = genOptionsForTcl(options);
oss << "myTclProc1 " << optionsForTcl << endl;
if(Tcl_Eval(interp, oss.c_str() == TCL_OK) {
// print out some messages to the GUI message window
}
...
}
The down side of this design:
Hard to debug tcl code error. Since every change in tcl code needs to re-compile the C code. Although a fast way is to write and test tcl code in tcl interactive shell. But a lot of tcl code is populated in C++, not hard-coded. So it's not so feasible.
Whole bunch of tcl code is written in C++ routines. This makes it hard to maintain.
I want to seek some insights from the community.
Can't you use the Tk toolkit called as library functions from C++?
Also, there is Tk/C++ - don't know how good it is. They overloaded operator- and use expression templates so that the C++ code like like Tcl. Pretty cool!