TCL not recognizing C++ Commands NS2 - c++

For starters, I had this working a week ago before new software was installed on my computer. Files were previously run with ./FileName.tcl instead of ns FileName.tcl. No idea why that changed, but I have not been able to get NS2 files to run the same again.
I added to the NS2 DumbAgent protocol to get energy of nodes. This was done via the following command in dumbagent.cc:
int DumbAgent::command(int argc, const char*const* argv)
{
if (argc == 3) {
//Other pre-built commands
else if (strcmp(argv[1], "get_energy") == 0)
{
u_int32_t addr = atoi(argv[2]);
get_energy(addr);
Node* iNode = Node::get_node_by_address(addr);
std::cout << iNode << std::endl;
printf("COMMMAND TEST ");
iEnergy = iNode->energy_model()->energy();
std::cout << iEnergy << std::endl;
return TCL_OK;
}
}
return Agent::command(argc, argv);
}
double DumbAgent::get_energy(u_int32_t addr)
{
Node* iNode = Node::get_node_by_address(addr);
std::cout << iNode << "\n";
Tcl& tcl = Tcl::instance();
printf("FUNCTION TEST ");
//addr = addr;
tcl.evalf("puts \"Node Reference from C++: %d \"", addr);
tcl.evalf("puts \"iNode from C++: %i \"", iNode);
double iEnergy;
iEnergy = iNode->energy_model()->energy();
tcl.evalf("puts \"energy = %d\"", iEnergy);
return iEnergy;
}
Using the proper additions to dumbagent.h, object files, and make clean, ./configure/pathtoTCL -withVersion=tcl, make I was able to get all these print statements to spit out what I need - as it took me using all the prints to figure everything out.
However, now, I cannot get anything out. I even added additional prints to the constructor to see what was going on:
DumbAgent::DumbAgent() : Agent(PT_PING) {
printf("TEST TEST TEST");
std::cout << "Constructor called." << std::endl;
iEnergy = 0.0;
bind("energy_otcl", &iEnergy);
bind("addr_otcl", &addr);
}
Nothing - all the DumbAgents and the simulation work. But no output. My Tcl Script calls everything with:
for {set i 0} {$i < $val(nn)} { incr i} {
set dumbagent($i) [new Agent/DumbAgent]
$ns attach-agent $node($i) $dumbagent($i)
}
#$dumbagent(1) get_energy 1
for {set i 0} {$i < $val(nn)} { incr i} {
$dumbagent($i) get_energy $i
puts "$dumbagent($i), $node($i), $i"
}
#$ns at 19.0 "$dumbagent(2) get_energy 2"
Where the first loop, use to print out the constructor items, and the second would print current energy. What is happening here? How can I get my information out?
I know it is calling the correct file because if I completely screw up the dumbagent.cc file, it will not make/configure. I either get no output at all (depending on lines I comment out in Tcl Script) or:
error "error when calling class $cls: $args" $..."
(procedure "_o758" line 2)
(SplitObject unknown line 2)
invoked from within
"$dumbagent($i) get_energy $i"
("for" body line 2)
invoked from within
"for {set i 0} {$i < $val(nn)} { incr i} {
$dumbagent($i) get_energy $i
puts "$dumbagent($i), $node($i), $i"
}"
What is going on?

Related

warning: Failed to call `main()` to execute the macro

I am trying to learn ROOT and I have a few codes that I can work with. Sometimes codes work but sometimes they don't.
{
c1 = new TCanvas("c1", "My Root Plots",600, 400);
c1->Divide(2,2);
c1->cd(1);
f=new TF1("f","[0]*exp(-0.5*((x-[1])/[2])**2)/(sqrt(2.0*TMath::Pi())*[2])",-100,100); f->SetTitle("Gaus;X axis ;Y axis");
f->SetParameter(0,0.5*sqrt(2*TMath::Pi()));
f->SetParameter(1,8);
f->SetParameter(2,5);
f->SetLineColor(3);
f->SetMarkerColor(1);
f->SetMarkerStyle(kOpenStar);
f->SetMarkerSize(5);
f->Draw();
c1->cd(2);
f1 = new TF1("f1", "[0]*x+[1]", 0,50);
f1->SetParameters(10,4);
f1->SetLineColor(5);
f1->SetTitle("ax+b;x;y");
f1->Draw();
}
This is the code I am trying to do. Code is kinda working , ''what do you mean kinda working''. I mean it's giving me a graph but as you can see in the code I wrote ( f->SetMarkerColor(1);
f->SetMarkerStyle(kOpenStar);) But markers didn't appear on the graph. Terminal doesn't giving me any errors. Is it my ROOT library missing ? I cannot upload images because I am new here.
I have a another problem. I want to share it maybe it will help solving the problem that I have.
void testRandom(Int_t nrEvents=500000000)
{
TRandom *r1=new TRandom();
TRandom2 *r2=new TRandom2();
TRandom3 *r3=new TRandom3();
TCanvas* c1=new TCanvas("c1","TRandom Number Generators", 800,600); c1->Divide(3,1);
TH1D *h1=new TH1D("h1","TRandom",500,0,1); TH1D *h2=new TH1D("h2","TRandom2",500,0,1); TH1D *h3=new TH1D("h3","TRandom3",500,0,1); TStopwatch *st=new TStopwatch();
st->Start();
for (Int_t i=0; i<nrEvents; i++) { h1->Fill(r1->Uniform(0,1)); } st->Stop(); cout << "Random: " << st->CpuTime() << endl; st->Start();
c1->cd(1); h1->SetFillColor(kRed+1); h1->SetMinimum(0); h1->Draw();
for (Int_t i=0; i<nrEvents; i++) { h2->Fill(r2->Uniform(0,1)); } st->Stop(); cout << "Random2: " << st->CpuTime() << endl; st->Start();
c1->cd(2); h2->SetFillColor(kGreen+1); h2->SetMinimum(0); h2->Draw();
for (Int_t i=0; i<nrEvents; i++) { h3->Fill(r3->Uniform(0,1)); } st->Stop(); cout << "Random3:" << st->CpuTime() << endl;
c1->cd(3);
h3->Draw(); h3->SetFillColor(kBlue+1); h3->SetMinimum(0);
}
This is a another code I am trying to run. But this code doesn't work an it's giving me this error.
warning: Failed to call main() to execute the macro.
Add this function or rename the macro. Falling back to .L.
I tried different things. I tried ,
root [1] .x main.cpp
root [1] .L main.cpp
still giving me same error.
f->SetMarkerColor(1); f->SetMarkerStyle(kOpenStar);) But markers
didn't appear on the graph.
Try f->Draw("PL") instead of f->Draw() to make the markers visible.
warning: Failed to call main() to execute the macro.
Rename your file, it should be called testRandom.cpp instead of main.cpp
Then, you can execute it with .x testRandom.cpp.

C++ call functions internally

I'm working with following code which gives access to low level monitor configuration using Windows APIs
https://github.com/scottaxcell/winddcutil/blob/main/winddcutil/winddcutil.cpp
And I would like to create a new function that increases or decreases the brightness, I was able to do this using Powershell but since the C++ code looks somewhat easy to understand I want to have a crack at it and try my luck and hopefully integrate it with an ambient light sensor later.
The powershell code I have is as follows which works with above executable: (its very crude at this stage)
$cb = [int]([uint32]("0x" + ((C:\Users\Nick\WindowsScripts\winddcutil-main\x64\Release\winddcutil.exe getvcp 0 10) -join "`n").split(" ")[2]))
if ($args[0] -eq "increase") {
if ( $cb -ne 100) {
$nb = "{0:x}" -f ($cb + 10)
C:\Users\Nick\WindowsScripts\winddcutil-main\x64\Release\winddcutil.exe setvcp 0 10 $nb
}
} elseif ($args[0] -eq "decrease") {
if ( $cb -ne 10) {
$nb = "{0:x}" -f ($cb - 10)
C:\Users\Nick\WindowsScripts\winddcutil-main\x64\Release\winddcutil.exe setvcp 0 10 $nb
}
}
It gets current brightness and if argument given is "increase" and if brightness is not already 100 then adds 10, in case of "decrease" it subtracts 10. Values are coveted to and from hex to decimals.
I understand if I want to integrate this inside the C++ code directly I would have something like following:
int increaseBrightness(std::vector<std::string> args) {
size_t did = INT_MAX;
did = std::stoi(args[0]);
//0 is monitor ID and 10 is the feature code for brightness
//currentBrightness = getVcp("0 10")
//calculate new value
//setVcp("0 10 NewValue")
}
Ultimetaly I would like to call the executable like "winddcutil.exe increasebrightness 0" (0 being the display ID)
I can keep digging around on how to do the calculation in C++ but internally calling the functions and passing the arguments so far turned out to be very challenging for me and I would appreciate some help there.
you need to add a needed option here
line 164
std::unordered_map<std::string,std::function<int(std::vector<std::string>)>> commands
{
{ "help", printUsage },
{ "detect", detect},
{ "capabilities", capabilities },
{ "getvcp", getVcp },
{ "setvcp", setVcp},
{"increasebrightness ", increaseBrightness } // update here
};
to get current brightness you can't use getVcp api due to its result will be printed to stdout , it isn't returned via returned value, follow getVcp to get brighness value , use this
DWORD currentValue;
bool success = GetVCPFeatureAndVCPFeatureReply(physicalMonitorHandle, vcpCode, NULL, &currentValue, NULL);
if (!success) {
std::cerr << "Failed to get the vcp code value" << std::endl;
return success;
}
then
define your increaseBrightness like
int increaseBrightness(std::vector<std::string> args) {
size_t did = INT_MAX;
did = std::stoi(args[0]);
DWORD currentBrightness;
bool success = GetVCPFeatureAndVCPFeatureReply(
physicalMonitorHandle, vcpCode, NULL, &currentBrightness, NULL);
if (!success) {
std::cerr << "Failed to get the vcp code value" << std::endl;
return success;
}
//example + 10
auto newValue = did + 10;
success = setVcp({"0", "10", std::to_string(newValue)});
if(success)
{
// your handler
}
// 0 is monitor ID and 10 is the feature code for brightness
// currentBrightness = getVcp("0 10")
// calculate new value
// setVcp("0 10 NewValue")
}
test for passing argument:
https://godbolt.org/z/5n5Gq3d7e
note: make sure your have increaseBrightness's declaration before std::unordered_map<std::string,std::function<int(std::vector<std::string>)>> commands to avoid compiler's complaint

CppUnitTestFramework: Test Method Fails, Stack Trace Lists Line Number at the End of Method, Debug Test Passes

I know, I know - that question title is very much all over the place. However, I am not sure what could be an issue here that is causing what I am witnessing.
I have the following method in class Project that is being unit tested:
bool Project::DetermineID(std::string configFile, std::string& ID)
{
std::ifstream config;
config.open(configFile);
if (!config.is_open()) {
WARNING << "Failed to open the configuration file for processing ID at: " << configFile;
return false;
}
std::string line = "";
ID = "";
bool isConfigurationSection = false;
bool isConfiguration = false;
std::string tempID = "";
while (std::getline(config, line))
{
std::transform(line.begin(), line.end(), line.begin(), ::toupper); // transform the line to all capital letters
boost::trim(line);
if ((line.find("IDENTIFICATIONS") != std::string::npos) && (!isConfigurationSection)) {
// remove the "IDENTIFICATIONS" part from the current line we're working with
std::size_t idStartPos = line.find("IDENTIFICATIONS");
line = line.substr(idStartPos + strlen("IDENTIFICATIONS"), line.length() - idStartPos - strlen("IDENTIFICATIONS"));
boost::trim(line);
isConfigurationSection = true;
}
if ((line.find('{') != std::string::npos) && isConfigurationSection) {
std::size_t bracketPos = line.find('{');
// we are working within the ids configuration section
// determine if this is the first character of the line, or if there is an ID that precedes the {
if (bracketPos == 0) {
// is the first char
// remove the bracket and keep processing
line = line.substr(1, line.length() - 1);
boost::trim(line);
}
else {
// the text before { is a temp ID
tempID = line.substr(0, bracketPos - 1);
isConfiguration = true;
line = line.substr(bracketPos, line.length() - bracketPos);
boost::trim(line);
}
}
if ((line.find("PORT") != std::string::npos) && isConfiguration) {
std::size_t indexOfEqualSign = line.find('=');
if (indexOfEqualSign == std::string::npos) {
WARNING << "Unable to determine the port # assigned to " << tempID;
}
else {
std::string portString = "";
portString = line.substr(indexOfEqualSign + 1, line.length() - indexOfEqualSign - 1);
boost::trim(portString);
// confirm that the obtained port string is not an empty value
if (portString.empty()) {
WARNING << "Failed to obtain the \"Port\" value that is set to " << tempID;
}
else {
// attempt to convert the string to int
int workingPortNum = 0;
try {
workingPortNum = std::stoi(portString);
}
catch (...) {
WARNING << "Failed to convert the obtained \"Port\" value that is set to " << tempID;
}
if (workingPortNum != 0) {
// check if this port # is the same port # we are publishing data on
if (workingPortNum == this->port) {
ID = tempID;
break;
}
}
}
}
}
}
config.close();
if (ID.empty())
return false;
else
return true;
}
The goal of this method is to parse any text file for the ID portion, based on matching the port # that the application is publishing data to.
Format of the file is like this:
Idenntifications {
ID {
port = 1001
}
}
In a separate Visual Studio project that unit tests various methods, including this Project::DetermineID method.
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
TEST_CLASS(ProjectUnitTests) {
Project* parser;
std::string projectDirectory;
TEST_METHOD_INITIALIZE(ProjectUnitTestInitialization) {
projectDirectory = EXPAND(UNITTESTPRJ);
projectDirectory.erase(0, 1);
projectDirectory.erase(projectDirectory.size() - 2);
parser = Project::getClass(); // singleton method getter/initializer
}
// Other test methods are present and pass/fail accordingly
TEST_METHOD(DetermineID) {
std::string ID = "";
bool x = parser ->DetermineAdapterID(projectDirectory + "normal.cfg", ID);
Assert::IsTrue(x);
}
};
Now, when I run the tests, DetermineID fails and the stack trace states:
DetermineID
Source: Project Tests.cpp line 86
Duration: 2 sec
Message:
Assert failed
Stack Trace:
ProjectUnitTests::DetermineID() line 91
Now, in my test .cpp file, TEST_METHOD(DetermineID) { is present on line 86. But that method's } is located on line 91, as the stack trace indicates.
And, when debugging, the unit test passes, because the return of x in the TEST_METHOD is true.
Only when running the test individually or running all tests does that test method fail.
Some notes that may be relevant:
This is a single-threaded application with no tasks scheduled (no race condition to worry about supposedly)
There is another method in the Project class that also processes a file with an std::ifstream same as this method does
That method has its own test method that has been written and passes without any problems
The test method also access the "normal.cfg" file
Yes, this->port has an assigned value
Thus, my questions are:
Why does the stack trace reference the closing bracket for the test method instead of the single Assert within the method that is supposedly failing?
How to get the unit test to pass when it is ran? (Since it currently only plasses during debugging where I can confirm that x is true).
If the issue is a race condition where perhaps the other test method is accessing the "normal.cfg" file, why does the test method fail even when the method is individually ran?
Any support/assistance here is very much appreciated. Thank you!

Keep Lua state in a C++ environment to limit context switches

I'm having fun coding simple OpenGL demos and I recently decided to use Lua with my C++ engine in order to change the rendering dynamically without having to recompile on and on my project. Thus I can tweak more easily the rendering algorithm. But I know that my current rendering update functions are probably far from being efficient.
For the moment, I'm transfering a matrix from C++ to Lua, modifying it in a Lua script and sending it back to my C++ rendering engine. But I'm reloading the Lua script each time I get an update call from the C++ engine, and I'm losing all of the variable context. That means I'm always starting from scratch and my rendering is far from being smooth. I include some code sample below to explain what I'm doing. I am currently learning Lua with C++ embedding, so I know I still don't have the best practices.
update.lua
function transform(m)
amplitude = 1.5
frequency = 500
phase = 0.0
r = {}
for i = 1, #m do
r[i] = {}
for j = 1, #m[i] do
if (i % 2) then
r[i][j] = amplitude * math.sin(m[i][j] + phase)
else
r[i][j] = -amplitude * math.sin(m[i][j] + phase)
end
phase = phase + 0.001
end
end
return r
end
-- called by c++
function update()
m = pull()
r = transform(m)
push(r)
end
matrix.cpp
// pull matrix from lua point of view
static int pull(lua_State * _L)
{
_push(_L, &_m);
return 1;
}
// push matrix from lua point of view
static int push(lua_State * _L)
{
// get number of arguments
int n = lua_gettop(_L);
if(1 == n) {
_pull(_L, 1, &_m);
}
return 1;
}
void matrix::load_file(char * file, char * function)
{
int status;
// load the file containing the script we are going to run
status = luaL_loadfile(_L, file);
switch (status) {
case LUA_OK:
break;
case LUA_ERRFILE:
std::cout << "LUA_ERRFILE: " << lua_error(_L) << std::endl;
break;
case LUA_ERRSYNTAX:
std::cout << "LUA_ERRSYNTAX: " << lua_error(_L) << std::endl;
break;
default:
std::cout << lua_error(_L) << std::endl;
}
lua_getglobal(_L, function);
status = lua_pcall(_L, 1, 1, 0);
if (status != LUA_OK) {
std::cout << "error running file" << lua_error(_L) << std::endl;
}
}
void matrix::update()
{
load_file("lua/update.lua", "update");
}
I'm thinking of passing some arguments when calling the update() function, but I'm wondering if the C++ to Lua then back to C++ approach is correct and efficient. Especially considering the fact that I might transfer and modify huge matrix in Lua. I probably lack some embedded Lua knowledge to keep context while loading a script. Do you have some general advice on how I would improve my code ? I know that my current approach is overly complicated.
A quick fix would be to only load the file if it has been modified since the last frame:
static time_t last_modified = 0;
struct stat sbuf;
stat(file, &sbuf);
if (sbuf.st_mtime > last_modified) {
last_modified = sbuf.st_mtime;
status = luaL_loadfile(_L, file);
// etc
}
// Now call the function
lua_getglobal(_L, function);
status = lua_pcall(_L, 1, 1, 0);
OK, loading the chunk of the update() function into a global variable and having a global parameter table in the Lua script is the way to go. I achieved this using the following guidelines, and I will post the detailed steps below. Basically, loading the script entirely first ensures that all global variables are stored in the C++ context. Then storing the wanted function as an index allows us to run it again, while keeping the global variables in the script evolving on their own.
Step 1
First call luaL_loadfile once at init
Step 2
Run the script once using lua_pcall(_L, 0, 0, 0);
This ensures that the global variables, which are used as parameters in the Lua script are in memory.
Step 3
Store the Lua function. I managed to do it with the following C++ code:
void matrix::store(char * function)
{
lua_newtable(_L); // create table for functions
_idx = luaL_ref(_L, LUA_REGISTRYINDEX); // store said table in pseudo-registry
lua_rawgeti(_L, LUA_REGISTRYINDEX, _idx); // retrieve table for functions
lua_getglobal(_L, function); // retrieve function to store
if (lua_isfunction(_L, -1)) {
_f = luaL_ref(_L, -2); // store a function in the function table
}
else {
lua_pop(_L, 1);
std::cout << "can't find " << function << std::endl;
}
// table is two places up the current stack counter
lua_pop(_L, 1); // we are done with the function table, so pop it
std::cout << "idx: " << _idx << ", function: " << _f << std::endl;
}
Step 4
Call the stored function again when rendering using the following C++ function:
void matrix::run()
{
int status;
if (_f == -1) {
std::cout << "invalid function index " << _f << std::endl;
}
else {
lua_rawgeti(_L, LUA_REGISTRYINDEX, _idx); // retrieve function table
lua_rawgeti(_L, -1, _f); // retrieve function
//use function
status = lua_pcall(_L, 0, 0, 0); // 0 arguments, 0 results
if (status != LUA_OK) {
std::cout << "error running function" << lua_error(_L) << std::endl;
}
//don't forget to pop the function table from the stack
lua_pop(_L, 1);
}
}
Step 5 (optional)
If we set all the Lua parameters in a global table, we can retrieve them dynamically in C++ using the following piece of code:
void matrix::get_params(char * p)
{
lua_getglobal(_L, p);
lua_pushnil(_L);
int i = 0;
while(lua_next(_L,-2))
{
const char * key = lua_tostring(_L,-2);
double value = lua_tonumber(_L,-1);
lua_pop(_L,1);
std::cout << key << " = " << value << std::endl;
_h[i].key.assign(key);
_h[i].value = value;
i++;
}
lua_pop(_L, 1);
}
Where _his a simple dynamic structure defined as such:
typedef struct {
std::string key;
float value;
} hash;
I only use float, so this simple structure is convenient enough for my needs, and allows me to add lots of variables in my Lua script without bothering with a structure definition in C++. This way I can add as many parameters in my Lua table and do the maths when updating.
Step 6
Tweak the Lua script forever ! Et voila:
p = {
amplitude = 1.5,
frequency = 500,
phase = 0.0
}
function transform(m)
r = {}
for i = 1, #m do
r[i] = {}
for j = 1, #m[i] do
if (i % 2) then
r[i][j] = p.amplitude * math.sin(m[i][j] + p.phase)
else
r[i][j] = -p.amplitude * math.sin(m[i][j] + p.phase)
end
p.phase = p.phase + 0.001
end
end
return r
end
-- called by c++
function update()
m = pull()
r = transform(m)
push(r)
end
This solution fits my needs, but seems very complicated and inefficient. But it was a fine hacking session anyway.

How to pass array of integers from Tcl to C++? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
How do I send Tcl array to C++? I have written the following code:
Tcl:
set ns [new Simulator]
set n [$ns node]
$n set X_ 100
$n set Y_ 30
$n set Z_ 0
set x [$n set X_]
set y [$n set Y_]
set z [$n set Z_]
#after 2000
set b {12 2 3 4 5}
set aa [new "Application/Trust/ITLeach"]
$aa set bufer_ 1
$aa set allnode_ $n
$aa set X_ $x
$aa set Y_ $y
$aa set Z_ $z
$aa set ClausterHeadID_ [array get b] **#send array to c++**
$ns at 0.0 "$aa start"
puts $b
$ns run
ITLEACH.h:
#ifndef ns_ITLeach_h
#define ns_ITLeach_h
#include "app.h"
#include "node.h"
#include "tcl.h"
#include "mobilenode.h"
#include <iostream>
#include <fstream>
class ITLeach;
#define TCL_OK 0
class ITLeach : public Application {
public:
ITLeach();
virtual int command(int argc, const char*const* argv);
protected:
// need to define recv and timeout
void start();
int Buffer;
MobileNode * node ;
ofstream nodeSetting;
double XPos ;
double YPos ;
double ZPos ;
int ClausterHeadID [] ; //int array that passed from tcl file
int ClausterID [] ;
int id_node;
};
#endif
ITLEACH.cc:
/*
* ITLeach.cc
*
* Created on: Oct 29, 2013
* Author: root
*/
#include "ITLeach.h"
static class ITLeachClass : public TclClass {
public:
ITLeachClass() : TclClass("Application/Trust/ITLeach") {}
TclObject* create(int, const char*const*) {
return (new ITLeach());
}
} class_app_ITLeach;
ITLeach::ITLeach() : Application() {
Tcl_Obj *baObj = Tcl_NewObj();
bind("bufer_",&Buffer);
bind("allnode_",&node);
bind("X_",&XPos);
bind("Y_",&YPos);
bind("Z_",&ZPos);
bind("ClausterHeadID_",(int *) &ClausterHeadID); // call array from tcl
bind("ClausterID_",ClausterID);
bind("id_",&id_node);
}
int ITLeach::command(int argc, const char*const* argv) {
if (strcmp(argv[1], "start") == 0) {
ITLeach::start();
return(TCL_OK);
}
return(ITLeach::command(argc, argv));
}
void ITLeach::start()
{
//double x=0, y =0 , z =0;
nodeSetting.open("./leachnode.txt",fstream::app);
//node = (MobileNode*)Node::get_node_by_address(i);
//node->location()->getLocation(x,y,z);
//node->getLoc(&x,&y,&z);
nodeSetting << "id " << id_node << " x "<< XPos << " y " << YPos << " z " << ZPos <<"\n";
nodeSetting.close();
printf(" claster head number : %d \n" ,ClausterHeadID[1]);
printf("node number is : %d \n",Buffer);
}
I send array from Tcl with this code:
$aa set ClausterHeadID_ [array get b] **#send array to c++**
and receive array from C++ with this code:
bind("ClausterHeadID_",(int *) &ClausterHeadID); // call array from tcl
But it doesn't work, please help me.
If you've got that command bound to the string interface (i.e., the arguments arrive via int argc, char **argv) then you use Tcl_SplitList() to take apart the relevant argument (which might be argv[argc-1], i.e., the last argument) and then Tcl_GetInt() to retrieve an integer from each of those values. Those integers are the members of that Tcl list.
int listc;
char **listv;
if (Tcl_SplitList(interp, argv[argc-1], &listc, &listv) != TCL_OK) {
// wasn't a valid list!
return TCL_ERROR;
}
std::vector<int> theArray(listc, 0);
for (int i=0 ; i<listc ; i++) {
if (Tcl_GetInt(interp, listv[i], &theArray[i]) != TCL_OK) {
// wasn't an int in the list!
return TCL_ERROR;
}
}
This isn't very fast! For a faster way, you need to use the Tcl_Obj-based API (the Tcl_Obj is the fundamental Tcl first-class value type), starting with registering your implementation function correctly. After that, it's fairly easy to convert the above code:
int listc;
Tcl_Obj **listv;
if (Tcl_ListObjGetElements(interp, argv[argc-1], &listc, &listv) != TCL_OK) {
// wasn't a valid list!
return TCL_ERROR;
}
std::vector<int> theArray(listc, 0);
for (int i=0 ; i<listc ; i++) {
if (Tcl_GetIntFromObj(interp, listv[i], &theArray[i]) != TCL_OK) {
// wasn't an int in the list!
return TCL_ERROR;
}
}
The big difference? A Tcl_Obj knows whether it is holding a string or an integer (or a float or any number of other things) and so the Tcl runtime doesn't normally need to reparse or type-convert values, whereas if everything is a string, you do a lot of conversions. (It's common to say “Everything is a string” in Tcl, but that's inaccurate; the correct version is “Everything has a perfect string serialization, or is a named entity” but that's rather more verbose.)