0

I would like to create a singleton MATLAB class acting as a global registry. The registry should store objects (of a certain class derived from handle) addressed with unique names. I would like to access the properties of the stored classes conveniently without temporary variables, for example:

Registry.instance().addElement('name1', NewObject(...));
Registry.instance().get('name1').Value
Registry.instance().get('name2').Value = 1;

Reading out properties of the returned class can be circumvented by removing the () from instance:

 >> Equipment.instance.get('name1').Value

However, it does not seem easy to use assignments because as noted in the comments, dot-indexing can't be used directly on the output of a function without assigning to an intermediate variable.

What is the proper way to implement and use such a "singleton registry" in MATLAB?

It should be noted that the singleton class contains some logic which is called when adding elements to the list, logic to properly destroy the objects in the right order and other methods which iterate through the object list. For that reason, a "normal" containers.Map cannot be used.

Dev-iL
  • 23,742
  • 7
  • 57
  • 99
divB
  • 896
  • 1
  • 11
  • 28
  • Did you try using a simple [map](https://www.mathworks.com/help/matlab/ref/containers.map.html), or perhaps [dynamic properties](https://www.mathworks.com/help/matlab/matlab_oop/dynamic-properties-adding-properties-to-an-instance.html)? I can write a proper answer if this is what you're looking for. – Dev-iL Aug 28 '18 at 09:14
  • Note that you can't use dot-indexing directly on the output of a function without assigning to an intermediate variable, so you would have to build on the map class as suggested by @Dev-iL, since it looks like your `get` method would be a function of the class, disabling you from doing `get('...').Value`. – Wolfie Aug 28 '18 at 09:31
  • Dynamic properties I am not sure how they would help here? map would look promising but I can't get it working either. Suppose I create a property `devs` as `containers.Map` in my singleton class. Then `Equipment.instance.devs('name1').Value1=0.1` still gives me `Expected one output from a curly brace or dot indexing expression, but there were 10 results.` – divB Aug 28 '18 at 09:32
  • @Wolfie: Yes I think this is the underlying problem. As just written, I do not get it to work when I use a map class as member variable to my singleton class. Are you thinking of something else? Can I combine a map class with singleton? (I should mention that the singleton class will have additional methods and also properly destroying the objects in the right order) – divB Aug 28 '18 at 09:35
  • 1
    Possible duplicate of [How can I index a MATLAB array returned by a function without first assigning it to a local variable?](https://stackoverflow.com/questions/3627107/how-can-i-index-a-matlab-array-returned-by-a-function-without-first-assigning-it) - in particular [this answer](https://stackoverflow.com/a/18154053/3978545) which suggests `getfield`, you could also use `setfield` for assignment within a struct – Wolfie Aug 28 '18 at 09:37
  • @Wolfie: Yes this indeed solves the question why what I am doing does not work. I purposefully asked the question in a generic way "how to implement a singleton registry in MATLAB the proper way". I am more interested in the proper way to do this (rather than applying a hack). How would you do it? Would you just use temporary variables and call it the day? – divB Aug 28 '18 at 09:46
  • 1
    Opinionated questions (i.e. "what is the best way to do X") are off topic here, as are questions which are too broad (/ *generic*)... So I'm not sure this is answerable within the scope of SO. That said, yes I would use temporary variables or the get/set field functions if this was an internal operation, or a wrapper function with defined syntax to make access simpler if you're exposing this function for broader use. Also `getfield` and `setfield` have been documented since pre-R2006a, so they're not hacky, just seldom used. – Wolfie Aug 28 '18 at 09:58
  • 1
    Hi @divB , I am trying to understand your question and the utility the desired functionality. It seems like you want some sort of global preferences object (for an application perhaps?), but do not want to load it into your workspace. Is that correct? If so, you could definitely do this by creating a static class (not of `handle` superclass) that has methods to set, retrieve and modify [custom matlab preferences](https://www.mathworks.com/help/matlab/ref/setpref.html). I could work up an example if that is what you're after. Cheers! – Khlick Aug 28 '18 at 13:53
  • 1
    Did you know about [`getappdata`](https://www.mathworks.com/help/matlab/ref/getappdata.html) and [`getpref`](https://www.mathworks.com/help/matlab/ref/getpref.html)? – Cris Luengo Aug 28 '18 at 14:16

1 Answers1

3

This might be what you're looking for:

classdef (Abstract) Registry % < handle   <-- optional

  methods (Access = public, Static = true)

    function addElement(elementName, element)
      Registry.accessRegistry('set', elementName, element );
    end    

    function element = get(elementName)
      element = Registry.accessRegistry('get', elementName);
    end

    function reset()
      Registry.accessRegistry('reset');
    end
  end

  methods (Access = private, Static = true)

    function varargout = accessRegistry(action, fieldName, fieldValue)
    % throws MATLAB:Containers:Map:NoKey
      persistent elem;
      %% Initialize registry:
      if ~isa(elem, 'containers.Map') % meaning elem == []
        elem = containers.Map;
      end
      %% Process action:
      switch action
        case 'set'
          elem(fieldName) = fieldValue;
        case 'get'
          varargout{1} = elem(fieldName);
        case 'reset'
          elem = containers.Map;
      end        
    end
  end

end

Since MATLAB doesn't support static properties, one must resort to various workarounds, possibly involving methods with persistent variables, as is the case in my answer.

Here's a usage example of the above:

Registry.addElement('name1', gobjects(1));
Registry.addElement('name2', cell(1) );     % assign heterogeneous types

Registry.get('name1')
ans = 
  GraphicsPlaceholder with no properties.

Registry.get('name1').get    % dot-access the output w/o intermediate assignment
  struct with no fields.

Registry.get('name2'){1}     % {}-access the output w/o intermediate assignment
ans =
     []

Registry.get('name3')        % request an invalid value
Error using containers.Map/subsref
The specified key is not present in this container.
Error in Registry/accessRegistry (line 31)
          varargout{1} = elem(fieldName);
Error in Registry.get (line 10)
      element = Registry.accessRegistry('get', elementName); 
Dev-iL
  • 23,742
  • 7
  • 57
  • 99
  • Thank you. I had a similar class (based on the singleton pattern, i.e. `Registry.getInstance.addElement`, ...). In your proposal the assignment does not work either (e.g., `Registry.get('name1').foo = 1`). But based on the other comments I assume now that this is just not possible and the "proper" way to do it is just to use a temporary variable for the object when assigning values to it. – divB Aug 29 '18 at 05:43
  • 1
    @divB It might be possible if your object has a `set` method, in which case you can `set(Registry.get('name1'), 'foo', 1);`. See also: [`matlab.mixin.SetGet`](https://www.mathworks.com/help/matlab/ref/matlab.mixin.setget-class.html). – Dev-iL Aug 29 '18 at 07:43