I have tried many alternatives for this simple thing but could not get it work. I want user to define a table from Lua in the 1st step:
a={["something"]=10} -- key=something, value=10
Then, in the second step the user will call from Lua a function designed in C++:
b=afunction(a) -- afunction will be designed in C++
The C++ code:
int lua_afunction(lua_State* L)
{
int nargs = lua_gettop(L);
if(nargs>1) throw "ERROR: Only 1 argument in the form of table must be supplied.";
int type = lua_type(L, 1);
if(type!=LUA_TTABLE) throw "ERROR: Argument must be a table";
//Until here it works as expected
lua_pushnil(L); //does not work with or without this line
const char* key=lua_tostring(L,-2);
double val=lua_tonumber(L,-1);
return 0;
}
As evidenced from the code lua_type(L,1) the bottom of the stack is the table itself. I was assuming on top of the table, the key will reside and on its top the value. So the height of the stack is 3 with idx=-1 the value, idx=-2 the key. However, it seems that I can neither read the key ("something") nor the value (10). Any ideas appreciated.
You need to call lua_next(L,-2) after lua_pushnil(L).
You need lua_next because apparently you don't know the key in the table. So you have to use the table traversal protocol, which is to push the table, push nil, call lua_next(L,-2), and get the key and value on the stack. This works because the table contains only one pair.
If you knew the key in the table, you could just have called lua_gettable or lua_getfield, without calling lua_next and lua_pushnil.
Related
is there a way to implement bsearch() to find multiple instances of key.
for example: (obj*)bsearch(key=r,arr,elements,sizeof(obj),(int(*)(const void*, const void*)bcompare);
The code I currently wrote only finds the first instance and cannot proceed past the first found due to how it works.
getline(target,81);
if(strcmp(target,"exit") == 0 || strcmp(target, "") == 0) break;
p = (Info*)bsearch(target,list,num,sizeof(Info),(int(*)(const void*, const void*))bcompare);
int foundIndex = (int)(p-list);
if(!p){
err_dis1_win();
clrscr();
}
else{
display_record(p);
cout << "\n\n found at index " << foundIndex << "\n";
getch();
clrscr();
}
Variables:
p - is a pointer to object of class Info
target - arr of char
list - arr of obj
foundIndex - index of element found
Info - derived class from base class
**compare function
int bcompare(char *a,Info *b){
return(strncmpi(a, b -> get_name(), strlen(a)));
}
I cannot use other methods such as std::find or writing my own binary search function and have to use bsearch()
I have tried loops inside the else block, and the compare function using the varible foundIndex, as well as using a while loop on the return value looping through the obj list arr. Is there a way to start at a specific index. I appreciate any help. I am not looking for code but a general push in the right direction. Thank you.
Caveat - The current code compiles and runs as expected however, the functionality that I want, cannot be figured out by myself. Google and search on Stackoverflow has not produced an related issue.
Since bsearch() returns only one item, I interpret "find multiple instances of key" as "find the first instance of a key". The caller can then step forward through the array from that item to process each item matching the key, until it reaches the end or reaches an item that does not match.
If you must use the standard library's bsearch() function and persuade it to find the first item matching a given key, then all you really have to work with is the comparison function you present. bsearch() will return an item that matches the key according to that function, but if more than one item matches then there is no guarantee which one will be returned. You must ensure, then, that only the item you want matches.
You can approach that with an appropriate implementation of the comparison function, but there is a significant problem. The function will in some cases need to evaluate the item preceding the one specified to it, but it must not attempt to examine an item preceding the array's first. bsearch() does not itself convey any information about the array bounds to the comparison function.
There are at least two possible solutions, neither of them stellar.
Store the array lower bound in some well-known location that the function can access. For example, if the comparison function is a static member function, then maybe you would use a static variable of its class. But that is not thread-safe. You could do something similar with thread-local variables, but even then it's ugly. Either way, you have to be sure to set that variable appropriately before you call bsearch(), and that's ugly, too.
OR
Ensure that you never bsearch() for the first item. One way you could do that would be by checking preliminarily whether the first item matches (but not via the comparison function), and using it directly instead of calling bsearch() in the event that it does match. I'd wrap that in a method, myself, and if you must not do so then requiring that such a calling discipline be employed manually is also ugly.
Having chosen one of the above, you can implement a comparison function that looks at the previous item's key in addition to the specified item's. Something along these lines (which assumes the second alternative):
struct my_item {
int key;
void *data;
};
// bsearch() passes the target item as the first argument, and the one to compare
// to it as the second
int compare_items(const void *to_find, const void *to_check) {
const struct my_item *to_find_item = (const struct my_item *) to_find;
const struct my_item *to_check_item = (const struct my_item *) to_check;
// Check first how the key members are ordered
if (to_find_item->key < to_check_item->key) {
return -1;
} else if (to_find_item->key > to_check_item->key) {
return 1;
} else {
// The key members match, so check whether we're looking at the first
// such item.
const struct my_item *previous_item = to_check_item - 1;
// If the previous item's key does match, then we know the item we're
// looking for is an earlier one than we are presently checking.
return (previous_item->key == to_check_item->key) ? -1 : 0;
}
}
I am learning C++ at the moment and have an example program implemented with an array of objects data store. To make some other operations easier, I have changed the store to a vector. With this change I am now not sure of the best way to search the store to find an object based on a member accessor value.
Initially I used a simple loop:
vector<Composer> composers; // where Composer has a member function get_last_name() that returns a string
Composer& Database::get_composer(string last_name)
{
for (Composer& c : composers)
if (c.get_last_name().compare(last_name))
return c;
throw std::out_of_range("Composer not found");
}
This works just fine of course, but to experiment I wanted to see if there were vector specific functions that could also do the job. So far I have settled on trying to use find_if() (if there is a better function, please suggest).
However, I am not sure exactly the correct way to use find_if(). Based on code seen in online research I have replaced the above with the following:
vector<Composer> composers; // where Composer has a member function get_last_name() that returns a string
Composer& Database::get_composer(string last_name)
{
auto found = find_if(composers.begin(), composers.end(),
[last_name](Composer& c) -> bool {c.get_last_name().compare(last_name);});
if (found == composers.end())
throw out_of_range("Composer not found");
else
return *found;
}
This does not work. It does find a result, but it is the incorrect one. If an argument that matches, say the third composer's last name the function always returns the first item from the vector (if I pass an argument that doesn't match any last name the function correctly throws an exception)... what am I doing wrong?
You are on the right track, your lambda needs return statement. Also in such case you do not have to specify it's return type explicitly, it can be deduced:
find_if(composers.begin(), composers.end(),
[last_name](const Composer& c) { return c.get_last_name() == last_name);});
you original code should not compile or at least emit warning(s), you should pay attention to them.
Note: it is not clear how your original code worked if you tested it, it should be:
if (c.get_last_name().compare(last_name) == 0 )
or simply:
if (c.get_last_name() == last_name )
as std::string::compare() returns int -1 0 or 1, so your code searches for string that does not match variable last_name
With range-v3, you may use projection:
auto it = ranges::find(composers, last_name, &composers::get_last_name);
I have a map with some values. Then I have a function that returns some string, and this string will be a member of the keys in map. I need to retrieve the value based on the key and pass it to another function, which takes it as argument.
map<string,int> SymbolTable;
SymbolTable["R0"]=0;
SymbolTable["R1"]=1;
SymbolTable["R2"]=2;
SymbolTable["R3"]=3;
string value=getValue(); //this one will return something from R0 to R3
nextFunction(SymbolTable[value]); // this part is wrong
If I give value=="R0" or some static value, this is working as expected. But whenever I pass this dynamic value, it returns 0 always, so my nextFunction is taking 0 as argument.
I tried to output the return value from getValue() to check what it is returning, and it is correct. I have tried this and similar ways, but all gives me the same issue. Can someone guide me on what am I doing wrong here? TIY
If I give value=="R0" or some static value, this is working as
expected. But whenever I pass this dynamic value, it returns 0 always
It simply means the "dynamic value" you obtained does not exist as a key in the map. std::map's operator [] inserts a default constructed value if the associated key does not exist.
To check for the existence of value in your map, you can do:
string value=getValue();
if(SymbolTable.count(value)){
//key exists....
nextFunction(SymbolTable[value]); // this part should be correct now
}
or you can equally use std::map::find
Normally the Lua stack begins at index 1. However, I noticed a strange phenomenon when reading the stack address 0 provided by calling a cfunction.
--lua tables defined via Lua C API
h{ }
u{ }
f{ u{ s{} } }
--table calls
h(2)
u(3)
f.u.s(4)
All the above seen tables (h, u, and the nested s) have a __call metamethod pointing to the same cfunction. From that cfunction I'm reading/dumping the passed stack:
while(start_index <= lua_gettop(state)) {
switch(lua_type(state, start_index)) {
case LUA_TNUMBER:
std::cout << "LUA_TNUMBER:"<<lua_tonumber(state, start_index);
break;
//... dump for all the other types
When start_index starts at 1, the output is as expected: LUA_TABLE LUA_TNUMBER:3; It contains the table that contains the metamethod (or so I think) and the argument, 3.
However when start_index starts at 0, I'd imagine the result is not a valid Lua type, but it is. The results are inconsistent: When calling from Lua, index 0 is always a LUA_TNUMBER with the value 5.
When however calling from C++ with pcall (lua_getfield, lua_pushnumber, lua_pcall), index 0 yields the same LUA_TNUMBER(5) for calling f.u.s, but LUA_TABLE for h and u.
What is at index 0, why is it a valid Lua type, and why is its value so weirdly inconsistent?
0 is not a valid stack index, so you can't rely on finding anything there. It's kinda like taking your lua_State pointer and dereferencing (lua_State - 1) and asking what the value there is. It's garbage.
From the Lua manual:
Any function in the API that receives stack indices works only with valid indices or acceptable indices.
A valid index is an index that refers to a real position within the stack, that is, its position lies between 1 and the stack top (1 = abs(index) = top).
An acceptable index can be any valid index, including the pseudo-indices, but it also can be any positive index after the stack top within the space allocated for the stack, that is, indices up to the stack size.
(Note that 0 is never an acceptable index)
Looking at the source (see index2addr), it looks like if Lua is built with LUA_USE_APICHECK, your call would thrown an error.
I need a very simple c++ function that calls a lua function that returns an array of strings, and stores them as a c++ vector. The function can look something like this:
std::vector<string> call_lua_func(string lua_source_code);
(where lua source code contains a lua function that returns an array of strings).
Any ideas?
Thanks!
Here is some source that may work for you. It may need some more polish and testing. It expects that the Lua chunk is returning the array of strings, but with slight modification could call a named function in the chunk. So, as-is, it works with "return {'a'}" as a parameter, but not "function a() return {'a'} end" as a parameter.
extern "C" {
#include "../src/lua.h"
#include "../src/lauxlib.h"
}
std::vector<string> call_lua_func(string lua_source_code)
{
std::vector<string> list_strings;
// create a Lua state
lua_State *L = luaL_newstate();
lua_settop(L,0);
// execute the string chunk
luaL_dostring(L, lua_source_code.c_str());
// if only one return value, and value is a table
if(lua_gettop(L) == 1 && lua_istable(L, 1))
{
// for each entry in the table
int len = lua_objlen(L, 1);
for(int i=1;i <= len; i++)
{
// get the entry to stack
lua_pushinteger(L, i);
lua_gettable(L, 1);
// get table entry as string
const char *s = lua_tostring(L, -1);
if(s)
{
// push the value to the vector
list_strings.push_back(s);
}
// remove entry from stack
lua_pop(L,1);
}
}
// destroy the Lua state
lua_close(L);
return list_strings;
}
First of all, remember Lua arrays can contain not only integers but also other types as keys.
Then, you can import the Lua source code using luaL_loadstring.
At this point, the only requirement left is the "return vector".
Now, you can use lua_istable to check whether a value is a table(array) and use lua_gettable to extract the multiple fields(see http://www.lua.org/pil/25.1.html) and manually add them one by one to the vector.
If you can not figure out how to deal with the stack, there seem to be some tutorials to help you. To find the number of elements, I found this mailing list post, which might be helpful.
Right now, I don't have Lua installed, so I can't test this information. But I hope it helps anyway.
Not really an answer to your question:
I've had a lot of trouble when writing c++ <=> lua interface code with the plain lua c-api. Then I tested many different lua-wrapper and I really suggest luabind if you are trying to achieve anything more or less complex. It's possible to make types available to lua in seconds, the support for smart pointers works great and (compared to other projects) the documentation is more or less good.