I know that with C++ one can create DLL files that contain certain functions, which can then be imported into Excel (e.g. via VBA). Let us take the following C++ function
double __stdcall square_it(double &x)
{
return x*x;
}
which we assume is incorporated in square.dll so that we can use the following VBA import
Declare PtrSafe Function square_it Lib "square.dll" (ByRef x As Double) As Double
Private Sub TestSub()
MsgBox square_it(4.5)
End Sub
So my question is: is it possible to code a function in Scala and then call it from VBA in a similar fashion?
Scala is not going to be any different from Java here, and looking at Java questions such as Can you use Java libraries in a VB.net program? and Calling Java library (JAR) from VBA/VBScript/Visual Basic Classic, there are not good solutions to get the same level of integration you have with VBA/C++ in VBA/Java or VBA/Scala.
You can always use any external channel to comunicate between your VBA and your Scala, such at a shell, the file system, or even http, but that's not going to be as straightforward and efficient as your VBA/C++ example.
Here is what it could look like comunicating through a shell:
In example.scala:
object Example {
def main(args: Array[String]): Unit = {
val d = args.head.toDouble
println(d * d)
}
}
In example.vba:
Module Example
Sub Main()
Dim command As String
command = "scala /path/to/example.scala 123"
Range("A1").Value = CreateObject("WScript.Shell").Exec(command).StdOut.ReadAll
End Sub
End Module
Scala runs on the JVM, and VBA does not, Therefore there is no native way to pass objects between them.
There are two alternatives to communicate between Scala and VBA:
One option is to start a new process for each procedure call and parse the output, as #OlivierBlanvillain suggested in his answer.
I think that the preferred method, is communicating in web sockets, and particularly http using a web server.
Web Server
Use a scala web server (there are tons of them), for example scalatra, and each procedure call should be mapped to a http request to localhost:
Scala server code (with scalatra)
class ScalaCall extends ScalatraServlet {
get("/:func/:params") {
Calling function {params("func")} with parameters {params("params")}
}
}
VBA Client code
Public Function ScalaCall(ByVal func As String, ByVal params As String) As String
Dim WinHttpReq As Object
Set WinHttpReq = CreateObject("Microsoft.XMLHTTP")
WinHttpReq.Open "GET", "http://127.0.0.1/" & func & "/" & params, False
WinHttpReq.Send
If WinHttpReq.Status = 200 Then
ScalaCall = WinHttpReq.ResponseBody
End If
End Function
Calling ScalaCall("f","x") on VBA should invoke a request to the scala server, that will output Calling function f with parameters x
You could use Obba - A Java Object Handler for Excel, LibreOffice and OpenOffice.
Related
I want to get an Object within a Collection as a String without calling a function for it.
e.G.:
In java i can
System.out.print(objVarXY)
and the compiler will automatically call the objVarXY.toString() function (if implemented)
in VBA something like this
Debug.Print parameterListe.LList.Item(1)
will cause an error.
Debug.Print parameterListe.LList.Item(1).toString
will work, if i implemented a toString Subfunction.
But what if i dont know what kind of object will be inside my LList collection?
Debug.Print will implicitly attempt coerce the given value expression into a String for output.
When you Debug.Print an object, VBA attempts to Let-coerce the object into a value - if the object doesn't have a default member that ultimately yields a value that can be implicitly converted to a String, then you get run-time error 438 "object doesn't support this property or method" if the class doesn't have a default member.
If the object is user code (i.e. your own class module), and if it makes sense to do so, you could add a default member yourself, and have the class responsible for knowing how to represent itself as a String (note that the VB attribute is hidden in the VBE code panes, and must be edited outside the VBE - unless you're using Rubberduck, in which case you can simply add a #DefaultMember annotation and synchronize annotations/attributes):
'#DefaultMember
Public Function ToString() As String
Attribute ToString.VB_UserMemId = 0
'...
End Function
But all this does is give the class the ability to be essentially treated as a String, through implicit member calls. I'd call that something like an abuse a language feature (it's through this mechanism that Debug.Print Excel.Application outputs the application's Name, or Debug.Print adoConnection outputs the connection's ConnectionString property), since as you noted, you might as well just invoke that ToString method explicitly.
If the object doesn't know how to represent itself as a String, then something, somewhere will have to. In Java (IIRC) and .NET this would essentially be the default ToString implementation:
Debug.Print TypeName(objVarXY)
...which is rather useless, but that's essentially what ToString does by default.
Whether you're writing Java, C#, or VBA, there needs to be code responsible for knowing how to represent objVarXY as a String.
Sadly VBA doesn't do fancypants pattern matching, so we can't Select Case TypeOf obj like in C# (it only recently got that ability - don't know about Java), and since Select Case TypeName(obj) wouldn't be type-safe, I'd go with If...ElseIf:
Public Function Stringify(ByVal obj As Object) As String
If TypeOf obj Is Something Then
Dim objSomething As Something
Set objSomething = obj ' cast to Something interface
Stringify = objSomething.SomeProperty
ElseIf TypeOf obj Is SomethingElse Then
Dim objSomethingElse As SomethingElse
Set objSomethingElse = obj ' cast to SomethingElse interface
Stringify = objSomethingElse.AnotherProperty & "[" & objSomethingElse.Foo & "]"
'ElseIf TypeOf obj Is ... Then
' ...
Else
' we don't know what the type is; return the type name.
Stringify = TypeName(obj)
End If
End Function
Obviously if the collection always involves classes that you own, the better solution is to have each object know how to represent itself as a String value.
But, having user classes expose a ToString method on their default interface isn't ideal: since we don't know what type we're getting from the collection, all we have is Object and a late-bound call - and no compile-time guarantee that the class implements a ToString method, and no compiler warning if we try to invoke, say, ToStrnig.
The solution is to not put ToString on the classes' default interface, and formalize the behavior with some IString class module, which might look like this:
Option Explicit
Public Function ToString() As String
End Funtion
Yup, that's the whole class.
Now the user classes that need to be representable as strings, can do this:
Option Explicit
Implements IString
Private Function IString_ToString() As String
' todo: implement the method!
End Function
And now we can have early-bound assurance that the objects have a ToString method:
Dim o As Object
For Each o In MyCollection
If TypeOf o Is IString Then
Dim s As IString
Set s = o 'cast to IString interface
Debug.Print s.ToString
Else
Debug.Print TypeName(o)
End If
Next
At the end of the day there's no magic, regardless of what language you're using.
Something like that does not exist in VBA there are only the Type conversion functions like CStr() to convert eg. an Integer into a String.
If you eg need to convert a Collection into an Array you will need to use a function for that.
But what if I don't know what kind of object will be inside my LList collection
Then you will need to determine which object it is (you would probably expect eg 5 different possible objects) and do something like a Select Case for each different object type to convert this to a String.
These two subs do the same thing when inside a class.
Sub DemoMe( )
Me.AboutMe ' Calls AboutMe procedure.
End Sub
Sub DemoMe( )
AboutMe ' Does the same thing.
End Sub
What is the point? Does the Me keyword do anything? What is the preferred way of an object accessing its own members?
tldr; No, although there are situations where it can be useful.
From the VBA language specification (5.3.1.5):
Each procedure that is a method has an implicit ByVal parameter called
the current object that corresponds to the target object of an
invocation of the method. The current object acts as an anonymous
local variable with procedure extent and whose declared type is the
class name of the class module containing the method declaration. For
the duration of an activation of the method the data value of the
current object variable is target object of the procedure invocation
that created that activation. The current object is accessed using the
Me keyword within the <procedure-body> of the method but cannot be
assigned to or otherwise modified.
That's all it is, just a "free" local variable that refers to the specific instance that the method is being called on. This also happens to be the default context for the procedures during their invocation, so it can be omitted if the code is intended to operate on the current instance. Although as #HansPassant points out in the comment above, it also allows the editor to bind to the interface and provide IntelliSense.
That said, there are a couple instances where you would either want to or have to use it (this is by no means an exhaustive list):
Naming collisions:
If your class has a member that "hides" a built-in VBA function, it can be used to make the scope explicit:
Public Property Get Left() As Long
'...
End Property
Public Property Get Right() As Long
'...
End Property
Public Property Get Width() As Long
Width = Me.Right - Me.Left
End Property
Equity Checks:
Public Function Equals(other As Object) As Boolean
If other Is Me Then
Equals = True
Exit Function
End If
'...
End Function
Fluent Functions:
This can be a useful pattern for compositing objects - you perform an action, then return the instance of the class so they can be "chained". Excel's Range interface does this in a lot of cases:
Public Function Add(Value As Long) As Class1
'Do whatever.
Set Add = Me
End Function
Public Sub Foo()
Dim bar As New Class1
bar.Add(1).Add(1).Add 1
End Sub
Not any more than there are reasons to use this in Java, C#, or any other language: it's a reserved identifier that represents the current instance of the class - what you do with that is up to your imagination.
What is the preferred way of an object accessing its own members?
Indeed, an object doesn't need the Me keyword to access it own public interface. Same as this in other languages, I'd even call it redundant. However it can sometimes be a good idea to explicitly qualify member calls with Me, especially when the class has a VB_PredeclaredId attribute (e.g. any UserForm): referring to UserForm1 in the code-behind of UserForm1 yields a reference to the default instance of the class, whereas qualifying member calls with Me yields a reference to the current instance of that class.
Accessing Inherited Members
VBA user code can't do class inheritance, but a lot of VBA classes do have a base class. The members of UserForm when you're in the code-behind of UserForm1, and those of Worksheet when you're in the code-behind of Sheet1, aren't necessarily easy to find. But since the inherited members show up in IntelliSense/auto-complete, you can type Me. and browse a list of members inherited from the base class, members that you would otherwise need to know about in order to invoke.
A class creating an instance of itself inside itself? That I've never seen.
You're missing out! I do this all the time, to enable referring to the object instance held by a With block, inside a Factory Method - like this GridCoord class.
Public Function Create(ByVal xPosition As Long, ByVal yPosition As Long) As IGridCoord
With New GridCoord
.X = xPosition
.Y = yPosition
Set Create = .Self
End With
End Function
Public Property Get Self() As IGridCoord
Set Self = Me
End Property
Note that while the GridCoord class exposes a getter and a setter for both X and Y properties, the IGridCoord interface only exposes the getters. As a result, code written against the IGridCoord interface is effectively working with read-only properties.
Another use is to get the name of the class module, without needing to hard-code it. This is particularly useful when raising custom errors: just use TypeName(Me) for the Source of the error.
The Builder Pattern notoriously returns Me, which enables a "fluent API" design that makes it possible to write code that incrementally builds complex objects through chained member calls, where each member returns Me (except the final Build call, which returns the type of the class being built):
Dim thing As Something
Set builder = New ThingBuilder
Set thing = builder _
.WithFoo(42) _
.WithBar("test") _
.WithSomething _
.WithSomethingElse
.Build
#PBeezy : In addition to my comment :
Me, refers to the object it's coming from so AboutMe resides in the class. If you had another instance, say this is Class1, you'd have dim c as Class1, as soon as you create an instance of Class1 in Class1, you need to tell the compiler which class you are using, the holding class or the instance created in, where, me.class1.aboutme would be logically valid. You can also create, a class for each cell in a workbook, then you could refer to A1's class from B1's class. Also, if there is a public function/sub called AboutMe, this also helps.
Class (clsPerson)
Public c1 As clsPerson
Public strPersonName As String
Public Function NAME_THIS_PERSON(strName As String)
strPersonName = strName
End Function
Public Function ADD_NEW_CHILD(strChildName As String)
Set c1 = New clsPerson
c1.strPersonName = strChildName
End Function
Normal module
Sub test()
Dim c As New clsPerson
c.NAME_THIS_PERSON "Mother"
c.ADD_NEW_CHILD "Nathan"
Debug.Print c.strPersonName
Debug.Print c.c1.strPersonName
End Sub
Gives these results
Mother
Nathan
I am thinking to extends the strings functions and add my own functions to the module. but I found that the strings class is non-inheritable.
is there any way to extends the class. I found something like :
Class MyStringType
Dim str As String
function toTitleCase()
'return titlecae
end function
End Class
Dim s As MyStringType
s.str = "mystringgoeshere"
but this does not extends in the proper way, it will results to :
S.Str.normalStringFunctions
S.MyOwnFunctionsHere
in result, the functions are not in the same level !!
any idea?
You can write Extension methods also in VB.NET
Module MyExtensions
<Extension()>
Public Sub toTitleCase(input As String)
....
End Sub
End Module
After this 'syntactic sugar' is in place you can write your code as
Dim newString = oldString.toTitleCase()
However, I would to remember you that ToTitleCase method is already available in the System.Globalization.TextInfo namespace
What you're after are extension methods.
Basically, you cannot extend System.String, but you can create subs or functions in modules and have the compiler "fake" they are string methods.
I have an access database and I'm attempting to write some VBA to increase automation.
I have a module I've entitled Global Variables which I've successfully used to define global constants (file paths etc) and a module ReportCode which has two main subrouties, one to run a query with ADODB (scraping form params where needed - returning a recordset), and a second which takes this record set and writes the data out to an excel template.
Given I may want to have multiple queries write to multiple tabs I thought the best way was to define a ExportDocument object to contain common parameters and a OrgReport object, containing query and tab specific parameters - then gather multiple OrgReport objects in a collection.
I'd hope to then pass just these two parameters into the main subroutine. This turns out to be a pain in VBA (or at least compared to ruby!).
Here you can see how I've defined by custom objects
Option Private Module
' Define Custom Doc Object
Public Type ExportDocument
TeamName As String
TemplatePath As String
SaveName As String
SavePath As String
End Type
' Define Custom Report Object
Public Type OrgReport
Query As String
Fields As Variant
Sheet As String
StartCol As Integer
StartRow As Integer
Headers As Boolean
End Type
And here is the code in my form which then called an additional module which does the heavy lifting - I know that part works because it did before I tried to go all OOP on this...
Private Sub my_report_from_form_Click()
' Prep Query Inputs
Dim TeamX_Report As OrgReport
TeamX_Report.Query = "qry_TeamReporting Query"
TeamX_Report.Sheet = "RawData"
TeamX_Report.StartCol = 1
TeamX_Report.StartRow = 2
TeamX_Report.Headers = True
TeamX_Report.Fields = Nothing
' Prep Document Inputs
Dim Teamx_Doc As ExportDocument
Teamx_Doc.TeamName = "MyTeam"
Teamx_Doc.TemplatePath = strReportTemplatePath & "MyTeam.xltm"
Teamx_Doc.SaveName = ""
Teamx_Doc.SavePath = strReportSavePath & Teamx_Doc.TeamName
' Init and set collection for CHAIN reports
Dim TeamReports As New Collection
TeamReports .Add Item:=TeamX_Report, Key:=TeamX_Report.Query
Call export_data_dump(Teamx_Doc, TeamReports)
End Sub
This gives me the issue of:
Only public user defined types defined in public object modules can be
used as parameters or return types for public procedures of class
modules or as fields of public user defined types
Following advice here I changed
Dim Teamx_Doc As ExportDocument
to
Teamx_Doc = CreateObject("ExportDocument")
But alas now i get
Run-time error '429': ActiveX component can't create object VBA
All references to this problem seem to be related to calling code from the Word., Excel. or Outlook. codebases, so perhaps I'm just missing a prefix for my own module stored within my database?
Best lead I've found is this one, which seems to suggest there's deeper issues with what i'm trying to do, or that i may get around parts by calling Friend, though I'm lost to where and how.
Is there a way I can late bind my UDT Objects within my form code, stash one in a collection then pass both to a subroutine that will be able to grab params from the first 'Doc' object and then iterate through the second 'report' object?
VBA >_<
There's no reason I can see why this doesn't work:
Dim Teamx_Doc As ExportDocument
Especially if you're not getting an error on line
Dim TeamX_Report As OrgReport
I've used custom Public Types before - no need for CreateObject
Though the docs seem to say it's just fine, can you try removing the
Option Private Module
The error message is kinda misleading. You simply can't put variables with an User-defined Type into a collection.
Option 1: Use an array instead. This actually sounds like it would work well for what you want to do.
Option 2: Create a class module instead of an UDT for OrgReport. Then instantiate objects of that class, those you can add to a collection.
See
Excel VBA Collections and Custom Data Types
and
http://www.mrexcel.com/forum/excel-questions/16849-adding-user-defined-types-collection.html
I like to convert an Object into a string. I found this topic but I can not get it to work in Excel 2010 ("Cast.xxx", "CArray()" and "Array()" seems to be unknown to Excel?)
I know how to convert a variant e.g. "toString = CStr(x)" but not how to do it the best way for an object
Function toString(ByVal x As Variant) As String
If TypeOf x Is Object Then
toString = ???
Else
toString = CStr(x)
End If
End Function
any suggestions?
Are you referring to the IFormattable interface. For example in VB.NET?
Excel VBA isn't a language that will provide you with a ToString method since it doesn't support inheritance.
In your example, you are simply converting whatever the variant is to a string, but an object in VBA wont support this directly.
If you have an external COM object, this would need to be exposed in the interface to access this method to call it from VBA.
For your own classes, you could implement your own IFormattable interface for your objects, but you would have assign to that interface before being able to call the objects ToString method - possible with a helper method. You could of course just supply a ToString method for every class which will through the class interface, but if you want to include this functionality for all objects, then an interface is probably the way to go.
Hope that helps.