0

So I have a class which inherits from the handle superclass like this:

classdef testClass < handle
    properties(Access = private)
        handles_gui;
    end

    methods(Access = public)
        function obj = testClass
            % Preferably like to get inputname here
            obj.handles_gui = obj.init_gui();
        end

        function callback_test(obj,hObject,eventdata)
            disp(inputname(1));
        end
    end

    methods(Access = private)
        function handles_gui = init_gui(obj)            
            handles_gui.figure = figure( ...
                'Tag', 'figure', ...
                'Units', 'characters', ...
                'Position', [50 35 167 25]);    

            handles_gui.button_left = uicontrol( ...
                'Parent', handles_gui.figure, ...
                'Units', 'characters', ...
                'Position', [41 1.2 8 1.8], ...
                'String', 'Test', ...
                'Callback', @(hObject,eventdata) callback_test(obj,hObject,eventdata)); 
        end
    end
end

I'd like to preferably obtain the object's workspace name in the constructor. Not sure if this is possible since I'm not sure if the name is assigned until after creation of the object. If thats the case, then I'd like to obtain it through a callback. I have a gui, but in order to properly pass the obj handle, I have to define the callback by passing obj in the init_gui function. This means that when inputname is called for callback_test when the button is pressed, it returns 'obj', since it's defined in the callback definition. But, if I call callback_test through the terminal, it returns the proper variable name (the results make sense, but it's not what I want). An example is shown below:

EDU>> test = testClass;
obj (this was called by clicking on the button)
EDU>> test.callback_test
test 
EDU>> 

So my question is: how can I obtain the variable name, preferably in the constructor, and if not, then how can I obtain it through the callback without having to use the terminal.

JustinBlaber
  • 4,629
  • 2
  • 36
  • 57
  • Just came up with an idea: you can potentially use `whos` and then find all variables names with class type you're interested in. Then you can `assignin` into the caller workspace and compare each variable with `obj` using `eq` to find the correct name. I'll see if this works later. – JustinBlaber Aug 02 '13 at 19:04
  • Well, my comment was wrong because you cannot use `assignin` within a nested function, so now I'm back to square one. – JustinBlaber Aug 02 '13 at 20:02
  • I show here how to retrieve the assignment variable name: http://stackoverflow.com/questions/17554012/in-matlab-is-it-possible-to-check-if-an-object-already-exists-before-creating-a/17601143#17601143. – Oleg Aug 02 '13 at 21:01
  • @OlegKomarov, I'll check this out later tonight. Thanks. – JustinBlaber Aug 02 '13 at 21:36
  • 1
    This is an interesting Matlab programming puzzle. But if you need the workspace name of the object within the object itself, then you probably have a broken architecture. This shouldn't be needed, and may prevent the user (i.e. you, in the future) from using this class in a flexible manner. – Pursuit Aug 02 '13 at 22:52
  • @Pursuit To give a little context, I have a program that uses a gui. The program is called using the form of "handle_class = class." Sometimes it's conventient to open two instances of the program as well. My idea was to be able to display the name of the handle in the figure title to make it easy to determine which figure belongs to which handle. I also haven't taken into account the possibility of a handle being deleted or overwritten but I was thinking it could be a useful utility. – JustinBlaber Aug 03 '13 at 01:41
  • I can understand that use. Explicit definition of names is still the way to go; variable names is going to be fragile and error-prone, if it's possible at all. (Plus it just hits my inelegant nerve really hard.) I've written up an explicitly defined name solution as an answer. – Pursuit Aug 03 '13 at 03:56

2 Answers2

0

If you need to know the assigned name of an object, then it is better to make that an explicit part of the constructor calling convention. For example:

        function obj = testClass(assignedName_input)
            obj.assignedName = assignedName_input;
            obj.handles_gui = obj.init_gui();
        end

Then, the use of the class changes to:

anyVariableName = testClass('test');  %Replaces "test = testClass();".  This separates the assigned name from the named used to keep track of it in the calling function.

Or, if you decide later on that you want 5 of these at once, in an array

for ix = 1:5
    arrayOfObjects{ix} = testClass(['test_' num2str(ix)]);  %This is not possible if you only look at the assigned variable name.
end

If, for some reason (and I can think of a few) you want to make sure that only one instance of each assignedName exists, you can use a static containers.Map within the class to maintain a list of existing objects, and a static factory method to create them. (I'm pretty sure this is the "factory method design pattern".)

For example, add:

properties(Access = private, Static = true)
    existingInstancesMap = containers.Map('keyType','char','valueType','any');
end

methods(Access = public, Static = true) 
    function mappedInstance = getInstance(assignedName);
    if ~existingInstancesMap.isKey(assignedName)                      %If no mapped instance exists
        existingInstancesMap(assignedName) = testClass(assignedName); %Then make one and map it.
    end
    mappedInstance = existingInstancesMap(assignedName);               %Return the mapped instance
end

And then make your constructor private.

Pursuit
  • 12,285
  • 1
  • 25
  • 41
0

Well, this is just incase someone else stumbles upon this problem. I solved it with:

classdef testClass < handle
    properties(Access = private)
        handles_gui;
    end

    methods(Access = public)
        function obj = testClass
            % Preferably like to get inputname here
            obj.handles_gui = obj.init_gui();
        end
    end

    methods(Access = private)
        function handles_gui = init_gui(obj)            
            handles_gui.figure = figure( ...
                'Tag', 'figure', ...
                'Units', 'characters', ...
                'Position', [50 35 167 25]);    

            handles_gui.button_left = uicontrol( ...
                'Parent', handles_gui.figure, ...
                'Units', 'characters', ...
                'Position', [41 1.2 8 1.8], ...
                'String', 'Test', ...
                'Callback', @(hObject,eventdata) callback_test(obj,hObject,eventdata)); 
        end

        function callback_test(obj,hObject,eventdata)
            basevars = evalin('base','whos');
            testClassvars = basevars(strcmp({basevars.class},class(obj)));

            found = false;
            for i = 1:length(testClassvars)
                if(eq(evalin('base',testClassvars(i).name),obj))
                    found = true;
                    disp(['Name is: ' testClassvars(i).name]);
                end
            end
            if(~found)
                disp('Handle has been deleted'); 
            end
        end
    end
end

This was the functionality I wanted; the trick was to use evalin to get access to objects of the same class in the base workspace. I thought I needed to use assignin to do this but was wrong. Whether or not it's good to implement something like this is a different question, but it's ultimately what I wanted to do, so it's here for soemone else who would like to do something similar.

Output:

EDU>> a = testClass

a = 

  testClass handle with no properties.
  Methods, Events, Superclasses

EDU>> b = testClass

b = 

  testClass handle with no properties.
  Methods, Events, Superclasses

Name is: b (after clicking on the button)
Name is: a (after clicking on the button)
JustinBlaber
  • 4,629
  • 2
  • 36
  • 57