Consulting

Results 1 to 9 of 9

Thread: Solved: Check if function has been called as sub

  1. #1

    Solved: Check if function has been called as sub

    Functions can be called as subs, in other words, without the calling code making use of the parameter; in yet other words, not on the right side of an = sign. I feel a function should know if that is the case. For instance, wouldn't it be nice and elegant if we could write the following?
    Function ConcatStrings _
    (ByRef s1 As String, ByVal s2 As String, Optional ByVal sSeparator As String = " ") _
    As String
    
    ConcatStrings = IIf(s1 = "", s2, s1 & sSeparator & s2)
        If I´mASub Then s1 = ConcatStrings    ' Don't change the parameter unnecessarily.
    End Function
    Where I´mASub is something Boolean (a hidden parameter, or a function) that tells the function whether it has been called as a sub. Is there anything like that?
    Last edited by Bob Phillips; 08-29-2020 at 02:10 AM. Reason: Updated code tags

  2. #2
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,438
    Location
    Why do you think this, what situation would this be useful for?
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  3. #3
    I thought my example illustrated nicely that it can be useful. Instead of having to write two almost identical procedures (one as a sub, and the other a function) that only differ in the way how they return the result, you need to write only one. This follows the ideal of code reuse.

    Moreover, having only one sub/function instead of two makes it easier to use, since you don't have to remember and deal with two different names for the same functionality. That is, I believe, the main reason why Microsoft allowed functions to be so easily used as subs.

    You might ask why I'm not simply leaving out the "If I´mASub Then" clause. Indeed, that's possible; VBA handles this undauntedly, even if s1 is a constant. But I consider that a hack. Each time a function changes its parameters, it opens a can of worms of possible unpleasant surprises. I generally try to catch my programming errors before they happen, which means, I don't change a parameter in a function unless I really mean it. Look at the following code; just changing a constant to a variable (a change that is not uncommon when a macro grows into a wider range of applicability) can lead to the same call giving different results. That spells trouble!
    Function ConcatStrings _
        (s1 As String, s2 As String, Optional sSeparator As String = " ") _
        As String
         
        ConcatStrings = IIf(s1 = "", s2, s1 & sSeparator & s2)
        s1 = ConcatStrings
    End Function
    
    Sub Test()
        Dim sLeft1 As String
        sLeft1 = "left"
        Const sLeft2 = "left"
        Const sRight = "right"
        Dim sSum As String
        
        Debug.Print "before", sLeft1, sLeft2
        sSum = ConcatStrings(sLeft1, sRight, "+")
        sSum = ConcatStrings(sLeft2, sRight, "+")
        Debug.Print "after", sLeft1, sLeft2
    End Sub
    Last edited by Bob Phillips; 08-29-2020 at 02:10 AM. Reason: Updated code tags

  4. #4
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,438
    Location
    No, I am asking what is the point? What is wrong with this.

    Sub TestFunction()
    Dim res1Of1 As Long
    Dim res1Of2 As Long
    Dim res2Of2 As Boolean
    
        Call OneOrTwo(3, res1Of1)
        MsgBox res1Of1
        res2Of2 = OneOrTwo(5, res1Of2)
        MsgBox res1Of2
        MsgBox res2Of2
        
    End Sub
    
    Function OneOrTwo(ByVal inp As Long, ByRef out As Long) As Boolean
    
        out = 2 * inp
        OneOrTwo = out > 5
    End Function
    One function to rule them all, one function to find them, one function to bring them all and in the darkness bind them ... does it all.
    Last edited by Bob Phillips; 08-29-2020 at 02:05 AM.
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  5. #5
    The point is that it would allow me to write clean code; code that avoids unpleasant surprises. See the last paragraph of my last post for how a harmless non-data change (such as changing a constant to a variable) can change the result of your macro. Maybe I should have added that that was meant to represent usage in a big project, as surprises are no problem in small macros.

    However, this discussion is beside the point of my question. My intention was to find out if such functionality exists. I guess my question has been answered now; if an experienced mentor such as you hasn’t heard of it, then it probably doesn’t exist. So, thank you for taking the time to think about it. I’ll leave the question active for a few more days, but if no one else has heard of that functionality, then I’ll close this as resolved.

  6. #6

    Check if function has been called as sub - a decade on

    Quote Originally Posted by Sebastian H View Post
    Functions can be called as subs, in other words, without the calling code making use of the parameter; in yet other words, not on the right side of an = sign. I feel a function should know if that is the case. For instance, wouldn't it be nice and elegant if we could write the following?
    [vba]Function ConcatStrings _
    (ByRef s1 As String, ByVal s2 As String, Optional ByVal sSeparator As String = " ") _
    As String

    ConcatStrings = IIf(s1 = "", s2, s1 & sSeparator & s2)
    If I´mASub Then s1 = ConcatStrings ' Don't change the parameter unnecessarily.
    End Function
    [/vba] Where I´mASub is something Boolean (a hidden parameter, or a function) that tells the function whether it has been called as a sub. Is there anything like that?


    Quote Originally Posted by Bob Phillips View Post
    Why do you think this, what situation would this be useful for?

    Quote Originally Posted by Sebastian H View Post
    The point is that it would allow me to write clean code; code that avoids unpleasant surprises.

    However, this is beside the point of my question. My intention was to find out if such functionality exists. I guess my question has been answered now; if an experienced mentor such as you hasn’t heard of it, then it probably doesn’t exist. So, thank you for taking the time to think about it. I’ll leave the question active for a few more days, but if no one else has heard of that functionality, then I’ll close this as resolved.
    Came across this a decade later because I have the same need, and the lack of info from google searches suggest it isn't possible, but no harm asking right?

    The OP wanted to know if it was possible to detect whether a caller was invoking a function as a function or as a sub i.e. x=fn(a,b) compared to fn a,b. I have a use case for this where if a routine is called as a function I want to open a form as modal, not letting any other processing occur until the form is finished with, and return the results of the form processing. If it is called as a sub I will open the form modelessly, the user can go about their other business and I will raise an event with the results when the form is closed. I don't want to raise the event if it is called as a function.

    My current code can handle this but it relies on me sending an additional argument to the function just for this purpose (I send "return" if I want it to return a value).

    Anyone know of a way to detect if a function is called as a function or just as a sub?

  7. #7
    Distinguished Lord of VBAX VBAX Grand Master Bob Phillips's Avatar
    Joined
    Apr 2005
    Posts
    25,438
    Location
    Again, I have to ask why you need to know this? I cannot see why you would have two entry points to do the same thing. The only, spurious case I can think of is if you were providing an app and allowed a UDF to kick something off, but that seems wrong to me.
    ____________________________________________
    Nihil simul inventum est et perfectum

    Abusus non tollit usum

    Last night I dreamed of a small consolation enjoyed only by the blind: Nobody knows the trouble I've not seen!
    James Thurber

  8. #8
    Quote Originally Posted by Bob Phillips View Post
    Again, I have to ask why you need to know this? I cannot see why you would have two entry points to do the same thing. The only, spurious case I can think of is if you were providing an app and allowed a UDF to kick something off, but that seems wrong to me.

    Because it affects the control flow . Anything that calls it as a function will manage the response, so the form will be modal and the object that is created as part of that process will be returned and managed locally by the caller. If it is called as a sub then the caller just wants to kick off a process and continue on doing other stuff. The form will be modeless allowing the user to complete the activity and the resultant object will be sent to a central processing class to deal with it. Everything that happens in the form is the same in both cases.

    Thanks for asking, and any thoughts welcome, but I am not looking to re-architecture the application, just looking for a more elegant way to implement what is there now.

    Do you know if it actually possible?

  9. #9
    Moderator VBAX Sage SamT's Avatar
    Joined
    Oct 2006
    Location
    Near Columbia
    Posts
    7,812
    Location
    Not only impossible, but programmatically a very bad idea.

    This thread is now closed
    I expect the student to do their homework and find all the errrors I leeve in.


    Please take the time to read the Forum FAQ

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •