This topic contains 5 replies, has 3 voices, and was last updated by abakobo 1 week, 1 day ago.
February 26, 2019 at 12:18 am #16091
There has to be something that I’m not understanding about callbacks. I can’t find any documentation other than the fact that first class functions are supported, and numerous searches back here have yielded no explanations.
I’m trying to wrap the sqlite3 module into a much easier to use class, but I’ve met a roadblock with how to handle a callback function.
I’m currently trying to do the following (paired down for readability)
Monkey12345678Function CB:Int(data:Void Ptr, argc:Int, argv:char_t Ptr, azColName:char_t Ptr)Print "Inside callback function!"Return 0Endsqlite3_exec(db,query,CB,Null,Varptr err_msg) 'Fails at CBsqlite3_exec(db,query,Null,Null,Varptr err_msg)' Runs, but then I don't get a callback to store the retrieved data
The CB implementation matches exactly what the parameter definition is expecting. I’ve tried prefixing it with Varptr with no luck either. So how can I use my function “CB” as a parameter to the sqlite3_exec() function? If I change it to Null, then the function runs, so I know this is where the issue is, but can’t for the life of me figure out how to get it to work with a callback, and I’ve found no documentation at all to figure it out.
Thanks!February 26, 2019 at 12:35 am #16092
The only thing I can see that might affect it is the callback char_t params are defined as PTR PTR. Just a guess, only started playing with Monkey2 the other day and this is all new to me.
Monkey1Function sqlite3_exec:Int( sqlite3 Ptr, sql:CString, callback:Int( Void Ptr, Int, libc.char_t Ptr Ptr, libc.char_t Ptr Ptr ), Void Ptr, errmsg:libc.char_t Ptr Ptr )February 26, 2019 at 12:40 am #16093
Thanks for the response. I will give that a try.
I had noticed before that a passing a Ptr with a Varptr preface would show up as ptr ptr when hovered over, so I guess I was assuming that would have been expected behavior.
Thanks againFebruary 26, 2019 at 1:20 pm #16095
The fisrt class functions has never been a problem with callbacks, for me.
To get your ‘char_t Ptr Ptr’
you might want to use ‘VarPtr myChar_tArray.Data’ as ‘Array.Data’ is already a pointer to the first element of the array.Edit doesn’t work. I’ve put some working code on the codebox below.
Monkey12345678910111213141516171819202122#Import "<libc>"Function Main()Local baseStr:="y"Local cstr:=New libc.char_t[baseStr.Length+1] '1 char larger to put the null termination charbaseStr.ToCString(cstr.Data,baseStr.Length+1) 'writes the baseStr to the cstrLocal csDat:=cstr.Data 'VarPtr cstr.Data is not accepted by compiler! & won't accept VarPtr twice neitherLocal chptrptr:=Varptr csDatPrintstuff(chptrptr)EndFunction Printstuff(csptrptr:libc.char_t Ptr Ptr)Print "mx2 callback"Print String.FromCString(csptrptr)End
I’ve joined a little zip/demo code using external function pointers with first class function in various ‘directions’.
Attachments:February 27, 2019 at 12:34 am #16098
Thanks to both of you!
I finally have it figured out.
I do a lot of database work, and have been evaluating Monkey2 for a large project that I would like to make cross-platform. I find the current sqlite3 library very ugly and difficult to work with, so I created a very simple wrapper class that makes things so much easier for me. Here is the current state, though I do plan on making this more complete and turning this into a module. Thanks again for the help!
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138#Import "<sqlite>"#Import "<std>"#Import "<libc>"Using sqlite..Using libc..Using std..#Rem Monkeydoc SQLiteResult (convenience class)I found no good reason to create a separate SQLiteResult class when a List of String Maps containsall the functionality we should need (for now anyways). See @List for more information...TODO: Should I implement an Interface that accepts Map<String,String>,Map<String,Int>, Map<String,Bool> etc?#EndAlias SQLiteResult:List<Map<String,String>>#rem monkeydoc SQLite: A sqlite3 wrapper class for easy database interactionThe SQLite class wraps the monkey2 sqlite3 module into an easy-to-use interfacefor interacting with sqlite3 databases.#endClass SQLitePrivateField dbPath:StringField db:sqlite3 Ptr' Callback function for building a @SQLiteResult (List of String Maps)Function BuildResult:Int(data:Void Ptr, argc:Int, argv:char_t Ptr Ptr, azColName:char_t Ptr Ptr)Local M:=New Map<String,String>For Local i:= 0 To argc-1Local col:String=String.FromCString( azColName[i])Local val:String=String.FromCString( argv[i] )M.Add(col,val)NextCast<SQLiteResult>(data).AddLast(M)Return 0 ' 0=continue, 1= stop after this callback...EndPublic#rem monkeydoc Create a new SQLite database instanceDefaults to ":memory:", which is a special in-memory databaseNote that we must connect ceparately using @Connect()#endMethod New(_dbPath:String=":memory:")dbPath=_dbPathEnd#rem monkeydoc Connect to/Create a new databaseConnects to the database specified in the constructor.If the database does not exist, create a new one.#endMethod Connect:Bool()Local rc:=sqlite3_open( dbPath,Varptr db )If rc<>SQLITE_OKPrint "Failed to open DB: "+sqlite3_errmsg( db )Disconnect()Return FalseEndifReturn TrueEnd#rem monkeydoc Disconnect from the databaseDisconnect from the database specified in the constructor.You can always reconnect at any time...#endMethod Disconnect()sqlite3_close(db)End#rem monkeydoc Run a SQL Query and returl a resultMost commonly used for true "SELECT" queries.Returns a result that is a List<Map<String,String>>, Aliased as a "SQLite Result"#endMethod Query:SQLiteResult(query:String)Local errMsg:char_t PtrLocal R:=New SQLiteResultLocal rc:=sqlite3_exec(db,query,BuildResult,Cast<Void Ptr>(R),Varptr errMsg)If rc<>SQLITE_OKPrint "Failed to fetch data: "+String.FromCString(errMsg)Return NullEndifReturn REnd#rem monkeydoc Execute a SQL statement (no result is returned)Very similar to @Query, but best suited for UPDATE, INSERT and other statments that have no need to return a result#endMethod Execute:Bool(query:String)Local errMsg:char_t PtrLocal rc:=sqlite3_exec(db,query, Null, Null, Varptr errMsg)If rc<>SQLITE_OKPrint "Failed to execute: " + String.FromCString(errMsg)Return FalseEndifReturn TrueEndEnd' TEST NEW SQLite Wrapper Class!Function Main()Local db:=New SQLite("/home/viavacavi/Dropbox/monkey2/test.sql3")db.Connect()Local q:Stringq = "DROP TABLE IF EXISTS Cars;"q += "CREATE TABLE Cars(Id INT, Name TEXT, Price INT);"q += "INSERT INTO Cars VALUES(1, 'Audi', 52642);"q += "INSERT INTO Cars VALUES(2, 'Mercedes', 57127);"q += "INSERT INTO Cars VALUES(3, 'Skoda', 9000);"q += "INSERT INTO Cars VALUES(4, 'Volvo', 29000);"q += "INSERT INTO Cars VALUES(5, 'Bentley', 350000);"q += "INSERT INTO Cars VALUES(6, 'Citroen', 21000);"q += "INSERT INTO Cars VALUES(7, 'Hummer', 41400);"q += "INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);"db.Execute(q)db.Disconnect() ' Just testing the Disconnect() method here...db.Connect() ' Testing reconnecting...q="SELECT * FROM Cars"Local result:= db.Query(q)Print "Number of records: " + result.Count()For Local row:=Eachin resultPrint row.Get("Id") + ") " + row.Get("Name")Print "~t" + row.Get("Price")Nextdb.Disconnect() ' All done!EndFebruary 27, 2019 at 7:00 am #16099
Note that you can assign a class instance’s method to a first class function too.
Viewing 6 posts - 1 through 6 (of 6 total)
You must be logged in to reply to this topic.