Author Topic: Defining a LISP function  (Read 13470 times)

0 Members and 1 Guest are viewing this topic.

Grrr1337

  • Swamp Rat
  • Posts: 812
Defining a LISP function
« on: October 04, 2018, 08:05:14 AM »
Hey .NET developers,
I wanted to ask you if its possible to define a LISP function via C#/.NET , by providing the actual LISP code ?
I mean having the LISP code inside of the .NET code and writing it+defining it on-the-fly.

So basically like passing this into VLIDE's console and defining it:

Code - Auto/Visual Lisp: [Select]
  1. (defun C:test ( / str )
  2.   (if (setq str (getstring "\nType something: "))
  3.     (alert str)
  4.   )
  5.   (princ)
  6. )

Second question: how can you call the LISP function itself, again via .NET ?
« Last Edit: October 04, 2018, 08:08:35 AM by Grrr1337 »
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #1 on: October 04, 2018, 08:40:52 AM »
Hi,

You can simply do it this way: entering the code in the command line with Document.SendStringToExecute() method and call the LISP function with Document.SendStringToExecute() method (which does not run synchronously or with Application.Invoke() method (which runs synchronously and allows to get the LISP function return value.

Define the LISP:
Code - C#: [Select]
  1.         [CommandMethod("LISPDEF")]
  2.         public static void LispDefine()
  3.         {
  4.             var code = "(defun C:test ( / str ) " +
  5.                 "(if (setq str(getstring \"\nType something: \"))" +
  6.                 "(alert str)" +
  7.                 ")" +
  8.                 "(princ)" +
  9.                 ")";
  10.             var doc = Application.DocumentManager.MdiActiveDocument;
  11.             doc.SendStringToExecute(code + "\n", false, false, false);
  12.         }

Call the LISP:
Code - C#: [Select]
  1.         [CommandMethod("LISPCALL")]
  2.         public static void LispCall()
  3.         {
  4.             var args = new ResultBuffer(new TypedValue(1, "c:test"));
  5.             Application.Invoke(args);
  6.         }

But, IMO, you should avoid to mix LISP from .NET this way. Most of the this brings more problems than solutions.
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #2 on: October 04, 2018, 10:11:13 AM »
Hi gile, (I was secretly waiting for you to answer)

Define the LISP:
Code - C#: [Select]
  1. { code }

Call the LISP:
Code - C#: [Select]
  1. { code }

Thanks alot!
The reason of my question is because at the moment I'm not enough confident/comfortable to throw myself fully with .NET
I have learned a bit on how to display a windows form and gather inputs, (you know: Form > DCL)..

However after the inputs are collected, starts the scary part of the code - performing operations in ACAD (which I find easier to do with LISP for now).
So this should be the part with more coding, which means being thrown with many different exceptions, without idea on how to debbug them (because of the uncomfortable property I got).


But I agree with you on this -
But, IMO, you should avoid to mix LISP from .NET this way. Most of the this brings more problems than solutions.

My plan is slowly to delve into .NET, by reducing the gaps that I fill via LISP atm.
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8696
  • AKA Daniel
Re: Defining a LISP function
« Reply #3 on: October 04, 2018, 11:13:44 AM »
LispFunction attribute might be of some interest, allows for sending / returning lists and other goodies 

Code: [Select]
         [LispFunction("lisp01")]
        // Note: you can return types bool, int, double, Point2d, Point3d, ObjectID, TypedValue,
        // ResultBuffer or null to lisp
        static public ResultBuffer lisp01(ResultBuffer pArgs)
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            try
            {
                if (pArgs != null)
                {
                    List<TypedValue> inArray = new List<TypedValue>(pArgs.AsArray());
                    return new ResultBuffer(inArray.ToArray());
                }
            }
            catch (System.Exception ex)
            {
                _AcAp.Application.ShowAlertDialog(
                    string.Format("\nError: {0}\nStackTrace: {1}", ex.Message, ex.StackTrace));
            }
            return null;
        }

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #4 on: October 04, 2018, 11:18:59 AM »
If the issue is about dialog boxes, you can build the dialog box with .NET and expose it to LISP via a LispFunction (i.e. a method which is decored with the LispFunction attribute). If I had to mix .NET and LISP I'd rather do it this way

Here's a trivial example with a simple dialog box containing a TextBox and a NumericUpDown:

The Dialog class exposes the data as properties:
Code - C#: [Select]
  1. using System.Windows.Forms;
  2.  
  3. namespace LispDialogSample
  4. {
  5.     public partial class Dialog : Form
  6.     {
  7.         public Dialog()
  8.         {
  9.             InitializeComponent();
  10.  
  11.             // the following properties can be set in the [Design] tab
  12.             btnOk.DialogResult = DialogResult.OK;
  13.             btnCancel.DialogResult = DialogResult.Cancel;
  14.         }
  15.  
  16.         public string TextValue => textBox1.Text;
  17.  
  18.         public int NumericValue => (int)numericUpDown1.Value;
  19.     }
  20. }

The LispFunctions class defines the 'GetDataFromDialog' LISP function which open the dialog to get the user inputs and returns the data as a list (or nil if Cancel):
Code - C#: [Select]
  1. using System.Windows.Forms;
  2.  
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Runtime;
  5.  
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. namespace LispDialogSample
  9. {
  10.     public class LispFunctions
  11.     {
  12.         [LispFunction("GetDataFromDialog")]
  13.         public static ResultBuffer GetDataFromDialog(ResultBuffer resbuf)
  14.         {
  15.             var dialog = new Dialog();
  16.             var result = AcAp.ShowModalDialog(dialog);
  17.             if (result != DialogResult.OK)
  18.                 return null;
  19.             return new ResultBuffer(
  20.                 new TypedValue((int)LispDataType.Text, dialog.TextValue),
  21.                 new TypedValue((int)LispDataType.Int32, dialog.NumericValue));
  22.         }
  23.     }
  24. }
  25.  

While netloaded, you can use this function in your LISP routines:
Code - Auto/Visual Lisp: [Select]
  1. (if (setq data (getdatafromdialog))
  2.   (alert (strcat "Text: " (car data) "\nNumber: " (itoa (cadr data))))
  3.   (alert "Cancelled operation")
  4. )

EDIT: Daniel was faster...
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #5 on: October 04, 2018, 05:09:28 PM »
LispFunction attribute might be of some interest, allows for sending / returning lists and other goodies 

Code: [Select]
[LispFunction("lisp01")]
...

Thanks nullptr, it looks quite interesting -

Code - Auto/Visual Lisp: [Select]
  1. _$ lisp01
  2. #<SUBR @000000f9c7658908 <EXRXSUBR>>
  3. _$ (lisp01 5)
  4. (5)
  5. _$ (lisp01 1 2 3 4)
  6. (1 2 3 4)
  7. _$ (lisp01)
  8. nil
  9. _$ (lisp01 '("A" "B" "C" "D"))
  10. ("A" "B" "C" "D")
  11. _$ (lisp01 '((00 01 02 03)(10 11 12 13)(20 21 22 23)(30 31 32 33)))
  12. ((0 1 2 3) (10 11.0 12.0 13.0) (20 21 22 23) (30 31 32 33))
  13. _$ (lisp01 1 2 3 '(1 2 3) '(4 5 6))
  14. (1 2 3 (1.0 2.0 3.0) (4.0 5.0 6.0))
  15.  

Although, I'm not sure about its purpose. Also seems to throw exceptions when symbols, or list of symbols are passed: (lisp01 'abc) or (lisp01 '(a b c))
BTW it reminds me of the list LISP function, which provides similar returns.


If the issue is about dialog boxes, you can build the dialog box with .NET and expose it to LISP via a LispFunction (i.e. a method which is decored with the LispFunction attribute). If I had to mix .NET and LISP I'd rather do it this way

Indeed thats the issue!
Now I know a bit more (about the LispFunction attribute)..
Indeed I now prefer your way of mixing .NET and LISP.. still I'm happy that you revealed the alternative approach per my original question!


Here's a trivial example with a simple dialog box containing a TextBox and a NumericUpDown:

Thanks for this example, gile - Highly appreciated!
Although I got some errors on declaring the public variables from the dialog class -

Code - C#: [Select]
  1. public string TextValue => textBox1.Text;
  2. public int NumericValue => (int)numericUpDown1.Value;

Code: [Select]
Error 9 'LispDialogSample_Gile.Dialog.numericUpDown1' is a 'field' but is used like a 'type'
Managed to fix it, by defining them as properties inside of that class -

Code - C#: [Select]
  1. using System.Windows.Forms;
  2.  
  3. namespace LispDialogSample_Gile
  4. {
  5.   public partial class Dialog : Form
  6.   {
  7.     public string TextValue { get; }
  8.     public int NumericValue { get; }
  9.     public Dialog()
  10.     {
  11.       InitializeComponent();
  12.       // the following properties can be set in the [Design] tab
  13.       btnOk.DialogResult = DialogResult.OK;
  14.       btnCancel.DialogResult = DialogResult.Cancel;
  15.      
  16.     }
  17.    
  18.     private void btnOk_Click(object sender, EventArgs e)
  19.     {
  20.       TextValue = textBox1.Text;
  21.       NumericValue = (int)numericUpDown1.Value;
  22.     } // btnOk_Click
  23.   }
  24. }


Learned alot from your posts.. and sorry for my late reply (took me some time to digest).

If you guys don't mind an additional question appeared:
As you probably guessed I'm a lisp guy, that used DCL to collect user inputs.
But the main reason for me to decide a switch, aside of all the fancy control events and the unique form design was because of some of the controls, that DCL doesn't seem to have.
I'm talking about the datagridview control, that would build a string[,] type of array, or in LISP I'd call it matrix list of strings.
In other words prompting user with a table (which is nearly impossible to do with DCL).

So my question is how would you pass a string[,] array to the ResultBuffer(); to the LispDataType enum? (say if I initially constructed the string[,] from the DGV's cell values)

Sorry for asking again (don't want to spam the .NET forum section with LISP-related question threads).

« Last Edit: October 04, 2018, 05:12:47 PM by Grrr1337 »
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8696
  • AKA Daniel
Re: Defining a LISP function
« Reply #6 on: October 05, 2018, 12:08:25 AM »
As you probably guessed I'm a lisp guy, that used DCL to collect user inputs.

also, don't forget about Open DCL, I think it has a nice grid control.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #7 on: October 05, 2018, 02:20:52 AM »
Although I got some errors on declaring the public variables from the dialog class -

Code - C#: [Select]
  1. public string TextValue => textBox1.Text;
  2. public int NumericValue => (int)numericUpDown1.Value;

This is C#6 (or later) syntax for property get statement.
You can also write this:
Code - C#: [Select]
  1. public string TextValue
  2. {
  3.     get { return textBox1.Text; }
  4. }
  5. public int NumericValue
  6. {
  7.     get { return (int)numericUpDown1.Value; }
  8. }
[/quote]
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #8 on: October 05, 2018, 02:47:10 AM »
So my question is how would you pass a string[,] array to the ResultBuffer(); to the LispDataType enum? (say if I initially constructed the string[,] from the DGV's cell values)

As AutoLISP only knows (linked) lists as data structure, you have to convert a 2 dimension array into a list, typically a list of sublists where each sublist represent a row.

Code - C#: [Select]
  1.         [LispFunction("foo")]
  2.         public ResultBuffer Foo(ResultBuffer resbuf)
  3.         {
  4.             var array = new string[2, 3]
  5.             {
  6.                 {"a", "b", "c" },
  7.                 {"d", "e", "f" }
  8.             };
  9.  
  10.             return Array2dToList(array);
  11.         }
  12.  
  13.         private ResultBuffer Array2dToList(string[,] array)
  14.         {
  15.             var retVal = new ResultBuffer();
  16.             for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  17.             {
  18.                 retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  19.                 for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  20.                 {
  21.                     retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  22.                 }
  23.                 retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  24.             }
  25.             return retVal;
  26.         }
Speaking English as a French Frog

estanima

  • Guest
Re: Defining a LISP function
« Reply #9 on: October 05, 2018, 01:18:07 PM »
Tried that as well when I started.  Dive in friend, C#.net is easier than lisp (to me) once the curve is over :)

Hey .NET developers,
I wanted to ask you if its possible to define a LISP function via C#/.NET , by providing the actual LISP code ?
I mean having the LISP code inside of the .NET code and writing it+defining it on-the-fly.

So basically like passing this into VLIDE's console and defining it:

Code - Auto/Visual Lisp: [Select]
  1. (defun C:test ( / str )
  2.   (if (setq str (getstring "\nType something: "))
  3.     (alert str)
  4.   )
  5.   (princ)
  6. )

Second question: how can you call the LISP function itself, again via .NET ?

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #10 on: October 05, 2018, 03:58:18 PM »

As you probably guessed I'm a lisp guy, that used DCL to collect user inputs.

also, don't forget about Open DCL, I think it has a nice grid control.

Ah, right. I know its more lisp-friendly - but still decided to try out .NET :)



Although I got some errors on declaring the public variables from the dialog class -
Code - C#: [Select]
  1. public string TextValue => textBox1.Text;
  2. public int NumericValue => (int)numericUpDown1.Value;

This is C#6 (or later) syntax for property get statement.


I see, now I understand why I get random errors from VS when I copy-paste C# samples from stackoverflow.




As AutoLISP only knows (linked) lists as data structure, you have to convert a 2 dimension array into a list, typically a list of sublists where each sublist represent a row.

Code - C#: [Select]
  1.  { code }

I saw the .ListBegin and .ListEnd enums but wasn't sure how they must be used -
For me that example is golden, Thanks gile!



Tried that as well when I started.  Dive in friend, C#.net is easier than lisp (to me) once the curve is over :)

I don't know why, but this OOP is dang hard for me (although not impossible).
When I get exceptions that I cannot self-explain (even after reading the exception message),
then I seek for examples and perform the american saying "monkey see, monkey do"(or monkey copy), until I start to get why its like that.


For instance now (playing for hours) I've assembled this from gile's codes and expected that it would work...

Code - C#: [Select]
  1. // Basically this is an attempt to use the 'Array2dToList' method within gile's 'GetDataFromDialog' example (and the 2dArray is built from a dataGridView1 control)
  2. public class LispFunctions
  3. {
  4.   [LispFunction("GetDataFromDGV")]
  5.   public static ResultBuffer GetDataFromDGV(ResultBuffer resbuf)
  6.   { // No matter what I do, I get this exception: "Object reference not set to an instance of an object."
  7.     var array = new string[2, 3]
  8.     {
  9.       {"a", "b", "c" },
  10.       {"d", "e", "f" }
  11.     };
  12.     var dialog = new Dialog();
  13.     var result = AcAp.ShowModalDialog(dialog);
  14.     if (result != DialogResult.OK)
  15.     return null;
  16.     // return Array2dToList(array); // if I decide to return this, then I would get the actual array
  17.     return Array2dToList(dialog.arrayDGV); // I would get null everytime (but I should get something?? - check the dialog class)
  18.    
  19.   }
  20.  
  21.   private static ResultBuffer Array2dToList(string[,] array)
  22.   {
  23.     var retVal = new ResultBuffer();
  24.     for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  25.     {
  26.       retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  27.       for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  28.       {
  29.         retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  30.       }
  31.       retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  32.     }
  33.     return retVal;
  34.   }
  35.  
  36. }



Code - C#: [Select]
  1. public partial class Dialog : Form // the Form contains two buttons for ok and cancel, and a datagridview with few columns, so the user could input in a table
  2. {
  3.   public string[,] arrayDGV { get; }
  4.  
  5.   public Dialog()
  6.   {
  7.     InitializeComponent();
  8.     // the following properties can be set in the [Design] tab
  9.     dataGridView1.AllowUserToAddRows = true;
  10.     dataGridView1.AllowUserToDeleteRows = true;
  11.     btnOk.DialogResult = DialogResult.OK;
  12.     btnCancel.DialogResult = DialogResult.Cancel;
  13.   }
  14.  
  15.   private void btnOk_Click(object sender, EventArgs e)
  16.   {
  17.    
  18.     // https://stackoverflow.com/questions/25746659/how-to-get-datagridview-data-in-a-2d-array/25747273
  19.     // datagridview to a 2D array
  20.     string[,] arrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count]; // first of all create a 2D array varialble and count the rows and //column of data grid view
  21.     foreach (DataGridViewRow row in dataGridView1.Rows)
  22.     {
  23.       int i = row.Index;
  24.       foreach (DataGridViewColumn col in dataGridView1.Columns)
  25.       {
  26.         int j = col.Index;
  27.         string itm = dataGridView1.Rows[i].Cells[j].Value.ToString();
  28.         if (itm == null || itm == String.Empty || itm.Trim().Length == 0) // I've included this, just to make sure I would get something
  29.         arrayDGV[row.Index, col.Index] = "null";
  30.         else
  31.         arrayDGV[row.Index, col.Index] = itm;
  32.       }
  33.     }
  34.    
  35.   } // btnOk_Click
  36. }

(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #11 on: October 05, 2018, 05:23:09 PM »
Typically you do not handle the Click event for the OK and Cancel buttons, you only set their DialogResult property.
As the calling LispFunction creates an instance of the dialog, it can access to all its public instance members (properties and methods) and it's a better practice to expose the data this way.

Code - C#: [Select]
  1.     public partial class Dialog : Form
  2.     {
  3.         /// <summary>
  4.         /// Creates a new instance of Dialog
  5.         /// </summary>
  6.         public Dialog()
  7.         {
  8.             InitializeComponent();
  9.  
  10.             // the following properties can be set in the [Design] tab
  11.             dataGridView1.AllowUserToAddRows = true;
  12.             dataGridView1.AllowUserToDeleteRows = true;
  13.             btnOk.DialogResult = DialogResult.OK;
  14.             btnCancel.DialogResult = DialogResult.Cancel;
  15.         }
  16.  
  17.         /// <summary>
  18.         /// Returns the DataGridView contents as a 2D array
  19.         /// </summary>
  20.         /// <returns>A 2D array</returns>
  21.         public string[,] GridToArray()
  22.         {
  23.             int numRows = dataGridView1.RowCount - 1;
  24.             int numColumns = dataGridView1.ColumnCount;
  25.             var array = new string[numRows, numColumns];
  26.             for (int i = 0; i < numRows; i++)
  27.             {
  28.                 for (int j = 0; j < numColumns; j++)
  29.                 {
  30.                     var value = dataGridView1.Rows[i].Cells[j].Value;
  31.                     array[i, j] = value == null ? "null" : value.ToString();
  32.                 }
  33.             }
  34.             return array;
  35.         }
  36.     }

Code - C#: [Select]
  1.         [LispFunction("GetDataFromDDV")]
  2.         public ResultBuffer GetDataFromDGV(ResultBuffer resbuf)
  3.         {
  4.             using (var dialog = new Dialog())
  5.             {
  6.                 var result = AcAp.ShowModalDialog(dialog);
  7.                 if (result != DialogResult.OK)
  8.                     return null;
  9.                 return Array2dToList(dialog.GridToArray());
  10.             }
  11.         }
  12.  
  13.         private ResultBuffer Array2dToList(string[,] array)
  14.         {
  15.             var retVal = new ResultBuffer();
  16.             for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  17.             {
  18.                 retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  19.                 for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  20.                 {
  21.                     retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  22.                 }
  23.                 retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  24.             }
  25.             return retVal;
  26.         }
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #12 on: October 06, 2018, 07:03:55 AM »
Typically you do not handle the Click event for the OK and Cancel buttons, you only set their DialogResult property.
 
I understand now, unlike DCL to store the returned value from the dialog via (start_dialog) function,
here the result is just kept as a property of the dialog object, and can be accessed anytime.


As the calling LispFunction creates an instance of the dialog, it can access to all its public instance members (properties and methods) and it's a better practice to expose the data this way.
   
Your example is working (as expected)!

By the way everything works fine in my next attempt.. although I don't understand why I got "Object reference not set to an instance of an object." exception (in the previous code, before your reply).
I was switching the ResultBuffer Methods from static to non-static,
aswell providing the initially declared var array = new string[2, 3] { {"a", "b", "c" }, {"d", "e", "f" } }; if the fault was in dialog.arrayDGV


Based on your example code, I find it more human-readable to process the public properties of a class, via private methods.. and then exposing them -

Code - C#: [Select]
  1. // This is just another practice, writing all from scratch and based on gile's example
  2. public class LispFunctions
  3. {
  4.  
  5.   [LispFunction("GetDataFromDGV")]
  6.   public ResultBuffer GetDataFromDGV(ResultBuffer resbuf)
  7.   {
  8.  
  9.     // I still don't get why I had this exception in my previous attempt: "Object reference not set to an instance of an object."
  10.     // regardless if I used "var dialog = new Dialog();" or "using (var dialog = new Dialog())", doesn't seem to be a reason for this error
  11.  
  12.     // using (var dialog = new Dialog())
  13.     // {
  14.     var dialog = new Dialog();
  15.     var result = AcAp.ShowModalDialog(dialog);
  16.     if (result != DialogResult.OK)
  17.     return null;
  18.     return Array2dToList(dialog.arrayDGV);
  19.     // } // using
  20.  
  21.   } // public ResultBuffer
  22.  
  23.   #region Private Methods for the LispFunctions class
  24.  
  25.   private ResultBuffer Array2dToList(string[,] array)
  26.   {
  27.       if (array == null || array.Length == 0)
  28.       return null;
  29.       else
  30.       {
  31.           var retVal = new ResultBuffer();
  32.           for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  33.           {
  34.               retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  35.               for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  36.               {
  37.                   retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  38.               }
  39.               retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  40.           }
  41.           return retVal;
  42.       }
  43.   }
  44.  
  45.   #endregion
  46.  
  47. }
  48.  

Code - C#: [Select]
  1. // This is just another practice, writing all from scratch and based on gile's example
  2. public partial class Dialog : Form
  3. {
  4.   // public string[,] arrayDGV { get; }
  5.  
  6.   public string[,] arrayDGV
  7.   {
  8.     get { return GridToArray(dataGridView1); }
  9.   }
  10.   public Dialog()
  11.   {
  12.     InitializeComponent();
  13.     dataGridView1.AllowUserToAddRows = true;
  14.     dataGridView1.AllowUserToDeleteRows = true;
  15.     btnOk.DialogResult = DialogResult.OK;
  16.     btnCancel.DialogResult = DialogResult.Cancel;
  17.   } // public Dialog()
  18.  
  19.   #region private Methods for Dialog class
  20.  
  21.   /// <summary>
  22.   /// Returns the DataGridView contents as a 2D array
  23.   /// </summary>
  24.   /// <returns>A 2D array</returns>
  25.   /// this version crashes if the DataGridView is empty
  26.   private string[,] GridToArray( DataGridView DGV ) // gile's subfoo version
  27.   {
  28.     int numRows = DGV.RowCount - 1;
  29.     int numColumns = DGV.ColumnCount;
  30.     var array = new string[numRows, numColumns];
  31.     for (int i = 0; i < numRows; i++)
  32.     {
  33.       for (int j = 0; j < numColumns; j++)
  34.       {
  35.         var value = DGV.Rows[i].Cells[j].Value;
  36.         array[i, j] = value == null ? "" : value.ToString();
  37.       }
  38.     }
  39.     return array;
  40.   } //  public string[,] GridToArray
  41.  
  42.  
  43.   // https://stackoverflow.com/questions/25746659/how-to-get-datagridview-data-in-a-2d-array/25747273
  44.   // datagridview to a 2D array
  45.   // this version crashes with fatal error, if the DataGridView is empty
  46.   private string [,] DataGridViewTo2Darray ( DataGridView DGV )
  47.   {
  48.     var arrayDGV = new string[DGV.Rows.Count, DGV.Columns.Count]; // first of all create a 2D array varialble and count the rows and //column of data grid view
  49.     foreach (DataGridViewRow row in DGV.Rows)
  50.     {
  51.       int i = row.Index;
  52.       foreach (DataGridViewColumn col in DGV.Columns)
  53.       {
  54.         int j = col.Index;
  55.         string itm = DGV.Rows[i].Cells[j].Value.ToString();
  56.         if (itm == null || itm == String.Empty || itm.Trim().Length == 0) // I've included this, just to make sure I would get something
  57.         arrayDGV[row.Index, col.Index] = "";
  58.         else
  59.         arrayDGV[row.Index, col.Index] = itm;
  60.       }
  61.     }
  62.     return arrayDGV;
  63.   } // private string [,] DataGridViewTo2Darray
  64.  
  65.   #endregion
  66.  
  67. }

Thank you once again, gile!
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #13 on: October 06, 2018, 01:25:23 PM »
Quote
although I don't understand why I got "Object reference not set to an instance of an object." exception (in the previous code, before your reply).

Because the public property 'Dialog.arrayDGV' is never instantiated.
You use a local variable also named arrayDGV in the btnOk_Click() method, but this variable is local to the method.
You should use the common naming convention for C#: use PascalCase for classes, methods, properties and camelCase for local variables and method parameters (see this topic).

Code - C#: [Select]
  1.     public partial class Dialog : Form // the Form contains two buttons for ok and cancel, and a datagridview with few columns, so the user could input in a table
  2.     {
  3.       public string[,] ArrayDGV { get; private set; } // the property is read only and set within the class by a method other tahn a constructor
  4.      
  5.       public Dialog()
  6.       {
  7.         InitializeComponent();
  8.         // the following properties can be set in the [Design] tab
  9.         dataGridView1.AllowUserToAddRows = true;
  10.         dataGridView1.AllowUserToDeleteRows = true;
  11.         btnOk.DialogResult = DialogResult.OK;
  12.         btnCancel.DialogResult = DialogResult.Cancel;
  13.       }
  14.      
  15.       private void btnOk_Click(object sender, EventArgs e)
  16.       {
  17.      
  18.         // https://stackoverflow.com/questions/25746659/how-to-get-datagridview-data-in-a-2d-array/25747273
  19.         // datagridview to a 2D array
  20.         //string[,] arrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count]; // first of all create a 2D array varialble and count the rows and //column of data grid view
  21.  
  22.         // instead of using a local variable, directly instantiate and set the ArrayDGV property
  23.         ArrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count];
  24.         foreach (DataGridViewRow row in dataGridView1.Rows)
  25.         {
  26.           int i = row.Index;
  27.           foreach (DataGridViewColumn col in dataGridView1.Columns)
  28.           {
  29.             int j = col.Index;
  30.             string itm = dataGridView1.Rows[i].Cells[j].Value.ToString();
  31.             if (itm == null || itm == String.Empty || itm.Trim().Length == 0) // I've included this, just to make sure I would get something
  32.             arrayDGV[row.Index, col.Index] = "null";
  33.             else
  34.             arrayDGV[row.Index, col.Index] = itm;
  35.           }
  36.         }
  37.      
  38.       } // btnOk_Click
  39.     }
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #14 on: October 06, 2018, 01:46:47 PM »
Two other remarks about "good practices" (which I sometimes forget to respect):
- always use a using statement to create an instance of a modal dialog ('closing' the modal dialog only hides it but does not call Close / Dispose so it remains in memory during all the session).
- Always prefer to use a static method for commands or LISP functions when an instance method is not absolutely necessary.
« Last Edit: October 06, 2018, 01:50:00 PM by gile »
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #15 on: October 08, 2018, 07:15:05 AM »
Once again, thank you gile!
Posts here are extremely useful for a lisper that decides to (atleast partially) transit into .NET

BTW I've heard before about the common naming convension in C# (more precisely in this (Bulgarian) lecture at 27:00),
but for me regardless of being aware of it, alot of coding practice is required (which I currently don't have for C#) to apply some standard (so apologies).
I find the link you posted is alot easier to follow (because its a quick do and do-not lecture).  8-)


Because the public property 'Dialog.arrayDGV' is never instantiated.

Ah now I see, Can you specify
was it because I messed-up the case: ArrayDGV with arrayDGV
or it didn't instantiated, because I used it as instance within that same class (... = new ...) -

Code - C#: [Select]
  1. ArrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count];


Sorry again for my late reply (draughtsman work appeared).
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #16 on: October 08, 2018, 08:54:20 AM »
Ah now I see, Can you specify
was it because I messed-up the case: ArrayDGV with arrayDGV
or it didn't instantiated, because I used it as instance within that same class (... = new ...) -

No the case is only a convention, but as C# is case sensitive, respecting these naming conventions may help to avoid confusion between private fields, local variable and method parameters on one hand and public properties on the other hand.
The property didn't instantiate because because you never initialize it, you only declare and initialize a local variable with the same name within the btnOk_Click() method, this variable is unknown outside the method:
Code - C#: [Select]
  1. public partial class Dialog : Form
  2. {
  3.     // a public property  named arrayDGV is declared but is not initialized
  4.     public string[,] arrayDGV { get; }
  5.      
  6.     //...
  7.      
  8.     private void btnOk_Click(object sender, EventArgs e)
  9.     {
  10.         // a local variable named arrayDGV is declared and initialized to a new instance of string[,]
  11.         // this variable is local to the method and has nothing to do with same named property
  12.         string[,] arrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count];
  13.         //...
  14.     }
  15. }


Basicaly, this is the way you use a private field (global variable within the class) and expose it outside the class with a public property:
Code - C#: [Select]
  1. public partial class Dialog : Form
  2. {
  3.     // a private field named arrayDGV is declared and not initialized
  4.     private string[,] arrayDGV;
  5.        
  6.     // a public property named ArrayDGV is declared and initialized to return the arrayDGV field value
  7.     public string[,] ArrayDGV
  8.     {
  9.         get { return arrayDGV; }
  10.     }
  11.      
  12.     //...
  13.      
  14.     private void btnOk_Click(object sender, EventArgs e)
  15.     {
  16.         // the private field named arrayDGV is initialized to a new instance of string[,]
  17.         arrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count];
  18.         //...
  19.     }
  20. }


Since C#3, a new syntax named 'auto-properties' allows to avoid declaring a private field (the private filed is effectively created when compiling in IL):
Code - C#: [Select]
  1. public partial class Dialog : Form
  2. {
  3.     // a public property named ArrayDGV is declared to be initialized from within the class (private set)
  4.     public string[,] ArrayDGV { get; private set; }
  5.      
  6.     //...
  7.      
  8.     private void btnOk_Click(object sender, EventArgs e)
  9.     {
  10.         // the ArrayDGV property is initialized as a new instance of string[,]
  11.         ArrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count];
  12.         //...
  13.     }
  14. }
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #17 on: October 10, 2018, 05:06:56 PM »
you never initialize it, you only declare and initialize a local variable with the same name within the btnOk_Click() method, this variable is unknown outside the method

After carefully reading your post:
"Ah now I understand! (atleast I think so)"
By default the declaration of the variables is private, so they are localised within the class or method, where they were declared.
As you know in LISP we manually have to declare them for every separate function (respectively method) - so this brought my confusion,
expecting that all of the declared variables would be local for the class and public for all the methods within that class :facepalm:

Apologies for my C# incompetence.. ended up with these self-explanations :

Code - C#: [Select]
  1. // Newbie C# self-explanations
  2. public class MyClass
  3. {
  4.   private string MyString1 = "String1"; // private and can be accessed within any method of MyClass
  5.   string MyString2 = "String2"; // private by default and can be accessed within any method of MyClass
  6.   public MyString3 = "String3"; // public and can be accessed outside of MyClass
  7.  
  8.   // #stupid commenting
  9.   // the method is private, so logic follows that
  10.   // everything declared within it is local for the method,
  11.   // and since the method itself is private then all the properties are private
  12.   // #end stupid commenting
  13.   // !!! All of the above comments are false, since even if the method is public - one can just call it, and cannot access any inner properties or other methods !!!
  14.   private void TheMethod()
  15.   {
  16.     int MyInteger = 1;
  17.     // ...
  18.   }
  19.   // Meaning that you cannot do.. ' MyClass.TheMethod().MyInteger ' - it just doesn't make any sense!
  20.   // Simply said: one can just call the method - MyClass.TheMethod();
  21.   // Although you can make the method to return something
  22.  
  23.   // Although you can define inner objects as inner-class:
  24.   public class Building
  25.   {
  26.     // Declaring public properties for the 'Building' object:
  27.     public int Floors { get; set; }
  28.     public int Rooms { get; set; }
  29.    
  30.     // Default values can be set for the properties:
  31.     Floors = 2;
  32.     Rooms = 6;
  33.    
  34.     // Declaring public methods for the 'Building' object:
  35.     public void AddFloors ()
  36.     {
  37.       // ...
  38.     }
  39.    
  40.     public void AddRooms ()
  41.     {
  42.       // ...
  43.     }
  44.    
  45.   }
  46.   // So a sample call in the main would be (I think so, might be wrong):
  47.   /*
  48.     class Program
  49.   {
  50.       static void Main()
  51.       {
  52.           Building building1 = new Building;
  53.           Building.Floors = 6;
  54.           Building.Rooms = 24;
  55.           Console.WriteLine("building1 Floors = {0} Rooms = {1}", building1.Floors, building1.Rooms);
  56.       }
  57.   }
  58.   */
  59.   // But thats what I came up with,
  60.   // an example https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/objects
  61.   // of creating a object through class
  62.   /*
  63.   public class Person // NOTE: this class is outter (its not nested in 'MyClass')
  64.   {
  65.       public string Name { get; set; }
  66.       public int Age { get; set; }
  67.       public Person(string name, int age)
  68.       {
  69.           Name = name;
  70.           Age = age;
  71.       }
  72.       //Other properties, methods, events...
  73.   }
  74.   */
  75.   // So a sample call in the main would be :
  76.   /*
  77.     class Program
  78.   {
  79.       static void Main()
  80.       {
  81.           Person person1 = new Person("Leopold", 6); // SEE THE DIFFERENCE? ALL THE PROPERTIES WERE SET WITHIN THE METHOD CALL
  82.           Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name, person1.Age);
  83.       }
  84.   }
  85.   */
  86.  
  87. }
 
Although I might be wrong somewhere in my statements (if so, correct me please)... but in the end looks like I have alot of reading and C# coding to do.
Really appreciate your help, gile!


Just one last general question for you - as being both: a LISP and .NET developer: (no offense to the other developers like gile)
If in LISP one could create a list, that could contain other lists or any type of mixed data, for instance:
'((<point1> <string1> <object1>)(<point2> <string2> <object2>)...(<pointN> <stringN> <objectN>))
...and you could map a custom function to every element of the list... (or just do whatever you want with it)
What would be the alternative type of data for this in C# ?

I'm asking because of the required strict declaration, such as
Code - C#: [Select]
  1. string[] SingleStringArray
  2. string[,] ArrayOfStringArrays
Guessing, would it be an array of object instances, where you would store (<pointN> <stringN> <objectN>) as properties for each one?
And if it doesn't matter if its array or a C# list or an collection, then what is the most suitable to map via function(respectively method).
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

JohnK

  • Administrator
  • Seagull
  • Posts: 10634
Re: Defining a LISP function
« Reply #18 on: October 10, 2018, 06:05:49 PM »
Without reading thread (only reading last post).
That would be a STRUCT.

Code - C#: [Select]
  1. public struct Cow  
  2. {  
  3.     public decimal weight;  
  4.     public string color;  
  5.     public string sex;  
  6. }


Public/Private ...welcome to the C based languages.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #19 on: October 10, 2018, 06:33:54 PM »
The main difference between LISP and C# is about dynamic vs static typing.
This also one of the main issue when mixing both laguages (with the execution context).
The ResultBuffer (or the TypedValue array) provides a way to communicate between LISP and .NET when collections of different types instance are necessary. Each TypedValue embed a Value property (of type Object) and a TypeCode property (of type short) which allows to easily cast the object to its type.
Speaking English as a French Frog

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Defining a LISP function
« Reply #20 on: October 11, 2018, 12:21:36 AM »
Without reading thread (only reading last post).
That would be a STRUCT.

Code - C#: [Select]
  1. public struct Cow  
  2. {  
  3.     public decimal weight;  
  4.     public string color;  
  5.     public string sex;  
  6. }


Public/Private ...welcome to the C based languages.


I haven't read the whole thread either but the crux of Lisp<->.net/C/C++ interop is just marshalling data using resbuf's (ResultBuffer in C#) as one parameter rather than many parameters, the rest is just a function call passing the data to the called function at either end. This called function can use this data and if required it then needs to build the result buffer with the results to return the values.
COM is similar but much more involved just to save building resbuf types of structures to marshall data back and forth but that's what's going on under the hood.

If you think of it like that it's easy :)
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

JohnK

  • Administrator
  • Seagull
  • Posts: 10634
Re: Defining a LISP function
« Reply #21 on: October 11, 2018, 07:26:09 AM »
Yeah I noticed that, guys. I guess I should have read the title of the thread too. I thought he was asking a different question at the end about lists in Lisp vs C# -ie. how you can "mix data types" in lisp but not C#. I apologize for my drive-by answer (next time it will be to issue a cat call).
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #22 on: October 11, 2018, 06:35:49 PM »
Without reading thread (only reading last post).
That would be a STRUCT.

Code - C#: [Select]
  1. public struct Cow  
  2. {  
  3.     public decimal weight;  
  4.     public string color;  
  5.     public string sex;  
  6. }


Public/Private ...welcome to the C based languages.

Thanks John,
If I properly understood I could create an array of struct instances and manipulate all of them within an iteration.
So respectively a (sub)list with items that stand for different properties, in C# thats a customly defined object with a customly defined properties/methods.
Well.. you know LISP aswell so you know from what mindset I'm switching to here.. (<mapcar_in_C#> 'MyMethod <Array/List/Collection>) :)



The main difference between LISP and C# is about dynamic vs static typing.

Ater I discovered for myself the static typing, started realising the fact -
That one could create a custom list, that can contain any variables aswell other lists..
so it could end up as uneven hierarchy of variables is now mind-blowing for me. i.e.:
Code - Auto/Visual Lisp: [Select]
  1.  '(0 2 "string" (123 nil <object> "I define" (("whatever") "I") "want" <function> (34 PI)))
After John's reply I now imagine that such random hierarchical structure of variables
would be possible to create in C# by defining classes/objects/methods (afterall C# is a OOP language <DUH>)
I brought that question from Reply #17, because afterall I'm comming from a dynamic typing language (if I blank-started a static type of langugage I probably wouldn't imagine such fancy structures soon)



This also one of the main issue when mixing both laguages (with the execution context).

Trying to work on that one,
At the moment I think a good practice for me would be working on a console application project,
where I add few Class(es) as separate .cs files and test how I'd print out different variables in the main.
Still would accept any other advises regarding on "how to understand the execution context in VS".



The ResultBuffer (or the TypedValue array) provides a way to communicate between LISP and .NET when collections of different types instance are necessary. Each TypedValue embed a Value property (of type Object) and a TypeCode property (of type short) which allows to easily cast the object to its type.


I haven't read the whole thread either but the crux of Lisp<->.net/C/C++ interop is just marshalling data using resbuf's (ResultBuffer in C#) as one parameter rather than many parameters, the rest is just a function call passing the data to the called function at either end. This called function can use this data and if required it then needs to build the result buffer with the results to return the values.
COM is similar but much more involved just to save building resbuf types of structures to marshall data back and forth but that's what's going on under the hood.

If you think of it like that it's easy :)

Now its more clear what gile did in his demo codes, thank you guys for the resume explanations!


I thought he was asking a different question at the end about lists in Lisp vs C# -ie. how you can "mix data types" in lisp but not C#. I apologize for my drive-by answer (next time it will be to issue a cat call).

Well you thought right, John.. and its not you -
I must apologise for the additional questions, its because one thought of mine led to another, and you guys are so generous answering so I felt lucky!
For short: redirected so much from the topic question that I ended up asking on how to bridge the gap LISP<->.NET

.. or just to get rid off some of the LISP limitations ... (first one was the datagridview) ..next one would be to prompt like this:

Which is far more effective user prompt than my GetStringList - with DCL

Didn't think I would push-off that soon without gile's help in here!
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

JohnK

  • Administrator
  • Seagull
  • Posts: 10634
Re: Defining a LISP function
« Reply #23 on: October 12, 2018, 09:42:14 AM »
Speaking in C language terms; you don't have to mapcar over a list like you do in Lisp. You can access objects (like the struct example) directly. [Cow.weight] However, you can create a list of Cows and use a foreach to iterate each cow object and pull the weight from each cow.

Speaking in C# in language `bridge mode'; when trying to share data between interfaces you need a middle-man and the word `bridge' you used is a pretty good analogy. The bridge is the ResultBuffer gile and MickD are talking about. In generic terms, not all datatypes can be shared as they are, often times they need to be TYPECAST back to their native container. That is to say, imagine that a NUMBER in C# cannot be "understood" by Lisp so you'd have to convert the NUMBER to STRING for sharing and back again to NUMBER again later.

This post was very generic and full of holes but I think it begins to answer some of your questions. I'll try again in a bit. Have you read any "learning C(plus, sharp, etc)" books? I'm thinking, these books will introduce you to the OO world, Data types, Inheritance, etc. a lot better then we can.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #24 on: October 12, 2018, 12:55:12 PM »
The C# equivalent function for mapcar is: IEnumerable<T>.Select() Linq extension method.
All instances of classes deriving from IEnumerable<T> contains elements of the same type (T) as a LISP list used with mapcar has to contain only elements of the type required by the function passed as argument.

Code - C#: [Select]
  1. var lst = new[] { 1, 2, 3, 4 };
  2. lst.Select(x => x * x).ToArray()
  3.  
returns: int[4] { 1, 4, 9, 16 }

Code - Auto/Visual Lisp: [Select]
  1. (setq lst '(1 2 3 4))
  2. (mapcar '(lambda (x) (* x x)) lst)
returns (1 4 9 16)
Speaking English as a French Frog

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Defining a LISP function
« Reply #25 on: October 12, 2018, 05:46:43 PM »
Speaking in C language terms; you don't have to mapcar over a list like you do in Lisp. You can access objects (like the struct example) directly. [Cow.weight] However, you can create a list of Cows and use a foreach to iterate each cow object and pull the weight from each cow.
...

To further this point a bit:

A result buffer is a struct type in C that holds 3 values:
A result buffer Type (typecode) and the value. The type code is used so the when C gets it it knows what type of value to cast the rusbuf value to. This way you don't try to cast an Int to a String etc.
The other value is a pointer to the next resbuf which enables you to create a Linked List of resbuf's and make them as long as you like and each value can be different. The way this is done in C is by using a Union, this gives the resbuf the memory size of the largest possible value in the union to make memory management easier.
To iterate over the linked list you just call resbuf->next().

Now, to pass values back and forth between Lisp and C say, you only need one generic function in the interpreter that takes the name of the function and the resbuf parameter (which is the start of the linked list). The interpreter looks up the function name in a symbol table (Dictionary of string to function pointer) for that function and passes the resbuf to it with the call.

This is a simplification but it's a pretty elegant way to interop between the two languages. The trick is to realise that the Lisp interpreter is written in C and has many helper functions to help with all this and in the end your Lisp code is executed in C (albeit a lot slower due to the read/eval process etc.). The same thing happens with .Net languages but there needs to but the .Net lang sits in between Lisp and C so wrappers need to be written.
These wrappers are already written in the .Net API and look a lot different to the C version but are more idiomatic to .Net (OOP) to make them easier to use.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8696
  • AKA Daniel
Re: Defining a LISP function
« Reply #26 on: October 13, 2018, 11:50:44 PM »
search for Tony T's  TypedValueList, its handy... also you can add implicit conversion operators to your classes

Code - C#: [Select]
  1. namespace Commands
  2. {
  3.     class TypedValueList : List<TypedValue>
  4.     {
  5.         public static implicit operator ResultBuffer(TypedValueList src)
  6.         {
  7.             return new ResultBuffer(src.ToArray());
  8.         }
  9.     }
  10.  
  11.     class foo
  12.     {
  13.         double m_dval;
  14.         string m_sval;
  15.  
  16.         public foo(double index, string value)
  17.         {
  18.             DVal = index;
  19.             SVal = value;
  20.         }
  21.  
  22.         public double DVal
  23.         {
  24.             get { return m_dval; }
  25.             set { m_dval = value; }
  26.         }
  27.         public string SVal
  28.         {
  29.             get { return m_sval; }
  30.             set { m_sval = value; }
  31.         }
  32.  
  33.         public static implicit operator ResultBuffer(foo src)
  34.         {
  35.             return (TypedValueList)src;
  36.         }
  37.  
  38.         public static implicit operator TypedValueList(foo src)
  39.         {
  40.             return new TypedValueList()
  41.             {
  42.                 new TypedValue((int)LispDataType.ListBegin),
  43.                 new TypedValue((int)LispDataType.Double, src.DVal),
  44.                 new TypedValue((int)LispDataType.Text, src.SVal),
  45.                 new TypedValue((int)LispDataType.ListEnd)
  46.             };
  47.         }
  48.  
  49.     }
  50.  
  51.     public class Commands
  52.     {
  53.         [LispFunction("doit")]
  54.         public static ResultBuffer doit(ResultBuffer args)
  55.         {
  56.             return new foo(1.0, "hello world");
  57.         }
  58.     }
  59. }
  60.  

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #27 on: October 18, 2018, 10:55:21 AM »
Thank you all !
Now I understood what OOP is all about - passing fields and using methods, between different classes.
Creating a list/array of object instances and iterating through it, by using for/foreach, where one can manipulate the properties of the objects inside,
and not the actual list/array - although it will contain the modified objects, but they still are the original ones that were declared.

..well technically one could do list manipulation in C#, following gile's comment via lst.Select and lst.CovertAll
  but now I see that it won't make much sense for OOP, or atleast would be an impractical approach, since mapping is redundant for a list of objects.
 
And like you all said the bridge to lisp<->.net is through the ResultBuffer class.
Apologies again for the caused offtopic, but your answers are really valuable for me!


BTW checking out at their definitions, helps alot -
 
Code - C#: [Select]
  1.   [Wrapper("resbuf")]
  2.   public sealed class ResultBuffer : DisposableWrapper, IEnumerable, IFormattable
  3.   {
  4.     public ResultBuffer();
  5.     public ResultBuffer(params TypedValue[] values);
  6.    
  7.     public static bool operator !=(ResultBuffer a, ResultBuffer b);
  8.     public static bool operator ==(ResultBuffer a, ResultBuffer b);
  9.    
  10.     public void Add(object value);
  11.     public void Add(TypedValue value);
  12.     public TypedValue[] AsArray();
  13.     public static ResultBuffer Create(IntPtr buffer, bool autoDelete);
  14.     protected override sealed void DeleteUnmanagedObject();
  15.     public override sealed bool Equals(object obj);
  16.     public ResultBufferEnumerator GetEnumerator();
  17.     public override sealed int GetHashCode();
  18.     public override sealed string ToString();
  19.     public string ToString(IFormatProvider provider);
  20.     public string ToString(string format, IFormatProvider provider);
  21.   }
  22.  
 
 
Code - C#: [Select]
  1.   public struct TypedValue
  2.   {
  3.     public TypedValue(int typeCode);
  4.     public TypedValue(int typeCode, object value);
  5.    
  6.     public static bool operator !=(TypedValue a, TypedValue b);
  7.     public static bool operator ==(TypedValue a, TypedValue b);
  8.    
  9.     public short TypeCode { get; }
  10.     [XmlElement("DwgHandle", typeof(Handle))]
  11.     [XmlElement("DwgInt16", typeof(Int16))]
  12.     [XmlElement("DwgInt32", typeof(Int32))]
  13.     [XmlElement("DwgInt64", typeof(Int64))]
  14.     [XmlElement("DwgObjectId", typeof(ObjectId))]
  15.     [XmlElement("DwgPoint3d", typeof(Point3d))]
  16.     [XmlElement("DwgReal", typeof(Double))]
  17.     [XmlElement("DwgText", typeof(String))]
  18.     [XmlElement("DwgVector3d", typeof(Vector3d))]
  19.     public object Value { get; }
  20.    
  21.     public override sealed bool Equals(object obj);
  22.     public override sealed int GetHashCode();
  23.     public override sealed string ToString();
  24.     public string ToString(IFormatProvider provider);
  25.   }
  26.  
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Defining a LISP function
« Reply #28 on: October 18, 2018, 01:06:14 PM »
As someone that is not familiar with AutoLisp, I am inferring AutoLisp doesn't have many other data structures besides lists?
I thought I read earlier in the thread that a point was (xx yy zz), how would a BlockTableRecord be represented in AutoLisp?
Is it a list of its properties & methods?

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: Defining a LISP function
« Reply #29 on: October 18, 2018, 02:31:42 PM »
As someone that is not familiar with AutoLisp, I am inferring AutoLisp doesn't have many other data structures besides lists?
I thought I read earlier in the thread that a point was (xx yy zz), how would a BlockTableRecord be represented in AutoLisp?
Is it a list of its properties & methods?

Pretty much, at least in pure LISP (ActiveX/VLISP is more method/property oriented).  Most work with lists of dotted pairs of values (Dictionary, with less control/limits), using a DXF integer code as the look-up.

LISP is primarily built around list-processing, so that's what you use/abuse to get the job done.  Just like using OOP for C#.
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #30 on: October 18, 2018, 02:57:13 PM »
As someone that is not familiar with AutoLisp, I am inferring AutoLisp doesn't have many other data structures besides lists?
I thought I read earlier in the thread that a point was (xx yy zz), how would a BlockTableRecord be represented in AutoLisp?
Is it a list of its properties & methods?

Talking about 'pure AutoLISP':
- the only AutoLISP  data structure is the LISP list (more accurately a single linked list base on cons cells)
- BlockTableRecord is represented with an ename (which is equivalent to the .NET ObjectId) and the BlockTableRecord properties can be accessed with the 'entget' function which returns a list of dotted pairs containing the object DXF data. These properties can be modified by editing the list and passing this new list to the 'entmod' function.

As LISP (on Windows systems) can acces to the AutoCAD ActiveX/COM API (typically called Visual LISP or vlisp)
- it can also use a data structure similar to arrays (called 'safearrays')
- BlockTableRecord is called a Block which has properties and methods.

dgorsman was faster...
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #31 on: October 18, 2018, 03:27:19 PM »
As someone that is not familiar with AutoLisp, I am inferring AutoLisp doesn't have many other data structures besides lists?
I thought I read earlier in the thread that a point was (xx yy zz), how would a BlockTableRecord be represented in AutoLisp?
Is it a list of its properties & methods?

Others were faster and probably explained better, still leaving my impressions (may worth something) :
It has primitive data types, like: int, string, real, null, list, symbol, subroutine, userSubroutine
Code - Auto/Visual Lisp: [Select]
  1. _$ (mapcar 'type (list 5 3.212 "Hello world" nil '("list" "of" "strings") 'daisy alert (lambda nil nil)))
  2. (INT REAL STR nil LIST SYM SUBR USUBR)
  3.  
  4. _$ (type (vl-catch-all-apply 'eval '((exit))))
  5. VL-CATCH-ALL-APPLY-ERROR
  6.  

But VLISP can offer access to acad's object model (COM).. so you still work with the built-in properties/methods, access the built-in objects
and there are additional datatypes such as: variant, safearray, vla-object.
However you can't create your own custom objects.
Also there are few ways to call the methods, with the
(<vla>-MethodName <vla-object> args)
or
(vlax-invoke <vla-object> 'MethodName args)
or
(vlax-invoke-method <vla-object> 'MethodName args)


Where in some you can provide the vanilla lisp representation of point, as a list of x/y/z numbers
but others require conversion with the built-in activex lisp function vlax-3d-point,
so it converts that list of '(x y z) into a variant of array of doubles with a value of safearray of doubles, that holds the x y z.
Code - Auto/Visual Lisp: [Select]
  1. (setq CurrentSpace (vla-get-Block (vla-get-ActiveLayout (vla-get-ActiveDocument (vlax-get-acad-object))))) ; obtain the current space, model/paper
  2. (setq pt '(10.0 20.5 5.2)) ; bound our point into a list of x y z
  3.  
  4. ; Few ways to call the same method that does the same thing:
  5. (vlax-invoke CurrentSpace 'AddPoint pt) ; #1 uses vanilla-lisp data as argument for the point
  6. (vla-AddPoint CurrentSpace (vlax-3d-point pt)) ; #2 converting the vanilla lisp point into a variant via (vlax-3D-point) function [required]
  7. ; #3 manual converting, to a variant data for 3D point [required]:
  8. (vla-AddPoint CurrentSpace
  9.   (vlax-make-variant
  10.       (vlax-make-safearray vlax-vbDouble '(0 . 2))
  11.       pt
  12.     )
  13.     (+ vlax-vbArray vlax-vbDouble)
  14.   )
  15. )
  16.  
  17. ; investigating what returns (vlax-3D-point) / or how it converts the input (thats how I came up with #3)
  18. _$ (vlax-3d-point '(10.0 20.5 5.2))
  19. #<variant 8197 ...>
  20. _$ (vlax-variant-type (vlax-3d-point '(10.0 20.5 5.2)))
  21. 8197
  22. ; https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2016/ENU/AutoCAD-AutoLISP/files/GUID-14029CC8-2839-432B-ABFC-F470AFA7606B-htm.html
  23. _$ (+ vlax-vbArray vlax-vbDouble)
  24. 8197
  25. ; hence:
  26. _$ (= (+ vlax-vbArray vlax-vbDouble) (vlax-variant-type (vlax-3d-point '(10.0 20.5 5.2))))
  27. T
  28. _$ (vlax-variant-value (vlax-3d-point '(10.0 20.5 5.2)))
  29. #<safearray...>
  30. _$ (safearray-value (vlax-variant-value (vlax-3d-point '(10.0 20.5 5.2))))
  31. (10.0 20.5 5.2)
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg