1 #ifndef OPENSIM_COMPONENT_SOCKET_H_
2 #define OPENSIM_COMPONENT_SOCKET_H_
3 /* -------------------------------------------------------------------------- *
4  *                       OpenSim:  ComponentSocket.h                          *
5  * -------------------------------------------------------------------------- *
6  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
7  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
8  * OpenSim is developed at Stanford University and supported by the US        *
9  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
10  * through the Warrior Web program.                                           *
11  *                                                                            *
12  * Copyright (c) 2005-2017 Stanford University and the Authors                *
13  * Author(s): Ajay Seth                                                       *
14  *                                                                            *
15  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
16  * not use this file except in compliance with the License. You may obtain a  *
17  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
18  *                                                                            *
19  * Unless required by applicable law or agreed to in writing, software        *
20  * distributed under the License is distributed on an "AS IS" BASIS,          *
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
22  * See the License for the specific language governing permissions and        *
23  * limitations under the License.                                             *
24  * -------------------------------------------------------------------------- */
25 
26 // INCLUDES
27 #include "osimCommonDLL.h"
28 
29 #include "ComponentPath.h"
30 #include "ComponentOutput.h"
31 #include "ComponentList.h"
32 #include "Object.h"
33 #include "Exception.h"
34 #include "Property.h"
35 
36 namespace OpenSim {
37 
38 //==============================================================================
39 /// ComponentSocket Exceptions
40 //==============================================================================
41 class InputNotConnected : public Exception {
42 public:
InputNotConnected(const std::string & file,size_t line,const std::string & func,const std::string & inputName)43     InputNotConnected(const std::string& file,
44                       size_t line,
45                       const std::string& func,
46                       const std::string& inputName) :
47         Exception(file, line, func) {
48         std::string msg = "Input '" + inputName;
49         msg += "' has not been connected.";
50         addMessage(msg);
51     }
52 };
53 
54 //=============================================================================
55 //                        OPENSIM COMPONENT SOCKET
56 //=============================================================================
57 
58 /**
59  * A Socket formalizes the dependency between a Component and another object
60  * (typically another Component) without owning that object. While Components
61  * can be composites (of multiple components) they often depend on unrelated
62  * objects/components that are defined and owned elsewhere. The object that
63  * satisfies the requirements of the Socket we term the "connectee". When a
64  * Socket is satisfied by a connectee we have a successful "connection" or is
65  * said to be connected.
66  *
67  * The purpose of a Socket is to specify: 1) the connectee type that the
68  * Component is dependent on, 2) by when (what stage) the socket must be
69  * connected in order for the component to function, 3) the name of a connectee
70  * that can be found at run-time to satisfy the socket, and 4) whether or
71  * not it is connected. A Socket maintains a reference to the instance
72  * (connectee) until it is disconnected.
73  *
74  * For example, a Joint has two Sockets for the parent and child Bodies that
75  * it joins. The type for the socket is a PhysicalFrame and any attempt to
76  * connect to a non-Body (or frame rigidly attached to a Body) will throw an
77  * exception. The connectAt Stage is Topology. That is, the Joint's connection
78  * to a Body must be performed at the Topology system stage, and any attempt to
79  * change the connection status will invalidate that Stage and above.
80  *
81  * Other Components like a Marker or a Probe that do not change the system
82  * topology or add new states could potentially be connected at later stages
83  * like Model or Instance.
84  *
85  * Programmatically, the connectee can be specified as an object reference
86  * or via a connectee path:
87  *
88  * @code{.cpp}
89  * // Specifying the connectee using an object reference.
90  * socket.connect(myConnectee);
91  * // Specifying the connectee via a path.
92  * socket.setConnecteePath("/path/to/connectee");
93  * @endcode
94  *
95  * Use finalizeConnection() to synchronize the object reference and connectee
96  * name. It is preferable to use connect() instead of setConnecteePath().
97  * If *both* are set, then the object reference overrides the connectee path.
98  *
99  * The connectee path appears in XML files and is how a connection is maintained
100  * across serialization (writing to an XML file) and deserialization (reading
101  * from an XML file).
102  *
103  * @author  Ajay Seth
104  */
105 class OSIMCOMMON_API AbstractSocket {
106 public:
107 
108     // default copy constructor, copy assignment
109 
~AbstractSocket()110     virtual ~AbstractSocket() {};
111 
112     /// Create a dynamically-allocated copy. You must manage the memory
113     /// for the returned pointer.
114     /// This function exists to facilitate the use of
115     /// SimTK::ClonePtr<AbstractSocket>.
116     virtual AbstractSocket* clone() const = 0;
117 
getName()118     const std::string& getName() const { return _name; }
119     /** Get the system Stage when the connection should be made. */
getConnectAtStage()120     SimTK::Stage getConnectAtStage() const { return _connectAtStage; }
121     /** Can this Socket have more than one connectee? */
isListSocket()122     bool isListSocket() const { return _isList; }
123     /** The number of slots to fill in order to satisfy this socket.
124      * This is 1 for a non-list socket. This is the number of elements in the
125      * connectee path property; to sync this with the number of connectee
126      * objects, call finalizeConnection(). */
getNumConnectees()127     unsigned getNumConnectees() const {
128         return static_cast<unsigned>(getConnecteePathProp().size());
129     }
130 
131     //--------------------------------------------------------------------------
132     /** Derived classes must satisfy this Interface */
133     //--------------------------------------------------------------------------
134     /** Is the Socket connected to its connectee(s)? For a list socket,
135     this is only true if this socket is connected to all its connectees.
136      */
137     virtual bool isConnected() const = 0;
138 
139     /** Get the type of object this socket connects to. */
140     virtual std::string getConnecteeTypeName() const = 0;
141 
142     /** Generic access to the connectee. Not all sockets support this method
143      * (e.g., the connectee for an Input is not an Object). */
getConnecteeAsObject()144     virtual const Object& getConnecteeAsObject() const {
145         OPENSIM_THROW(Exception, "Not supported for this type of socket.");
146     }
147 
148     /** Connect this %Socket to the provided connectee object. If this is a
149         list socket, the connectee is appended to the list of connectees;
150         otherwise, the provided connectee replaces the single connectee. */
151     virtual void connect(const Object& connectee) = 0;
152 
153     /** Find the connectee using a search with a partial path. Use this if you
154      * do not want to specify an exact path (maybe you don't quite know where
155      * the connectee is located).
156      * */
findAndConnect(const ComponentPath & connectee)157     virtual void findAndConnect(const ComponentPath& connectee) {
158         OPENSIM_THROW(Exception, "Not implemented.");
159     }
160     /** Same as findAndConnect(const ComponentPath&), but using a string
161      * argument. */
findAndConnect(const std::string & connectee)162     void findAndConnect(const std::string& connectee) {
163         findAndConnect(ComponentPath(connectee));
164     }
165 
166     /** Connect this %Socket according to its connectee path property
167         given a root %Component to search its subcomponents for the connect_to
168         Component. */
finalizeConnection(const Component & root)169     virtual void finalizeConnection(const Component& root) {
170         throw Exception("finalizeConnection() not implemented; not supported "
171                         "for this type of socket", __FILE__, __LINE__);
172     }
173 
174     /** Clear references to connectees. The connectee path property is not
175      * affected. Calling finalizeConnection() will use the connectee path
176      * property to satisfy the socket. */
177     virtual void disconnect() = 0;
178 
179     /** %Set connectee path. This function can only be used if this socket is
180      * not a list socket. If a connectee reference is set (with connect()) the
181      * connectee path is ignored; call disconnect() if you want the socket to be
182      * connected using the connectee path.
183      *
184      * It is preferable to use connect() instead of this function. */
setConnecteePath(const std::string & name)185     void setConnecteePath(const std::string& name) {
186         OPENSIM_THROW_IF(_isList,
187                          Exception,
188                          "An index must be provided for a list Socket.");
189         setConnecteePath(name, 0);
190     }
191 
192     /** %Set connectee path of a connectee among a list of connectees. This
193      * function is used if this socket is a list socket. If a connectee
194      * reference is set (with connect()) the connectee path is ignored; call
195      * disconnect() if you want the socket to be connected using the connectee
196      * name.
197      *
198      * It is preferable to use connect() instead of this function. */
setConnecteePath(const std::string & name,unsigned ix)199     void setConnecteePath(const std::string& name, unsigned ix) {
200         using SimTK::isIndexInRange;
201         SimTK_INDEXCHECK_ALWAYS(ix, getNumConnectees(),
202                                 "AbstractSocket::setConnecteePath()");
203         updConnecteePathProp().setValue(ix, name);
204     }
205 
206     /** Get connectee path. This function can only be used if this socket is
207     not a list socket.                                                     */
getConnecteePath()208     const std::string& getConnecteePath() const {
209         OPENSIM_THROW_IF(_isList,
210                          Exception,
211                          "An index must be provided for a list Socket.");
212         return getConnecteePath(0);
213     }
214 
215     /** Get connectee path of a connectee among a list of connectees.         */
getConnecteePath(unsigned ix)216     const std::string& getConnecteePath(unsigned ix) const {
217         using SimTK::isIndexInRange;
218         SimTK_INDEXCHECK_ALWAYS(ix, getNumConnectees(),
219                                 "AbstractSocket::getConnecteePath()");
220         return getConnecteePathProp().getValue(ix);
221     }
222 
appendConnecteePath(const std::string & name)223     void appendConnecteePath(const std::string& name) {
224         OPENSIM_THROW_IF((getNumConnectees() > 0 && !_isList), Exception,
225             "Multiple connectee paths can only be appended to a list Socket.");
226         updConnecteePathProp().appendValue(name);
227     }
228 
229     /** Clear all connectee paths in the connectee path property. */
clearConnecteePath()230     void clearConnecteePath() {
231         if (isListSocket())
232             updConnecteePathProp().clear();
233         else
234             updConnecteePathProp().setValue(0, "");
235     }
236 
237     /** Get owner component of this socket */
getOwner()238     const Component& getOwner() const { return _owner.getRef(); }
239 
240 protected:
241     //--------------------------------------------------------------------------
242     // CONSTRUCTION
243     //--------------------------------------------------------------------------
244     /** Create a Socket with specified name and stage at which it should be
245     connected.
246     @param name               name of the socket, usually describes its
247                               dependency.
248     @param connecteePathIndex Index of the property in the containing Component
249                               that holds this Socket's connectee path(s).
250     @param connectAtStage     Stage at which Socket should be connected.
251     @param owner              Component to which this Socket belongs. */
AbstractSocket(const std::string & name,const PropertyIndex & connecteePathIndex,const SimTK::Stage & connectAtStage,Component & owner)252     AbstractSocket(const std::string& name,
253                    const PropertyIndex& connecteePathIndex,
254                    const SimTK::Stage& connectAtStage,
255                    Component& owner) :
256             _name(name),
257             _connectAtStage(connectAtStage),
258             _connecteePathIndex(connecteePathIndex),
259             _owner(&owner),
260             _isList(getConnecteePathProp().isListProperty()) {}
261 
262     /** %Set an internal pointer to the Component that contains this Socket.
263     This should only be called by Component.
264     This exists so that after the containing Component is copied, the 'owner'
265     is the new Component. This Socket needs to be able to modify
266     the associated connectee path property in the Component. Thus, we require
267     a writable reference. */
268     // We could avoid the need for this function by writing a custom copy
269     // constructor for Component.
setOwner(Component & o)270     void setOwner(Component& o) { _owner.reset(&o); }
271     /** This will be false immediately after copy construction or assignment.*/
hasOwner()272     bool hasOwner() const { return !_owner.empty(); }
273 
274     /** Check if entries of the connectee path property's value is valid (if
275     it contains spaces, etc.); if so, print out a warning. */
checkConnecteePathProperty()276     void checkConnecteePathProperty() {
277         // TODO Move this check elsewhere once the connectee path
278         // property is a ComponentPath (or a ChannelPath?).
279         for (unsigned iname = 0u; iname < getNumConnectees(); ++iname) {
280             const auto& connecteePath = getConnecteePath(iname);
281             ComponentPath cp(connecteePath);
282             if (!cp.isLegalPathElement(cp.getComponentName()) ) {
283                 std::string msg = "In Socket '" + getName() +
284                         "', connectee path '" + connecteePath +
285                         "' contains illegal characters such as spaces.";
286                 if (!_isList) {
287                     msg += " Did you try to specify multiple connectee "
288                            "names for a single-value Socket?";
289                 }
290                 OPENSIM_THROW(Exception, msg);
291             }
292             // TODO update the above for Inputs when ChannelPath exists.
293 
294             // TODO There might be a bug with empty connectee path being
295             // interpreted as "this component."
296         }
297     }
298 
299 protected:
300 
301     /// Const access to the connectee path property from the Component in which
302     /// this Socket resides. The name of that property is something like
303     /// 'socket_<name>'. This is a special type of property
304     /// that users cannot easily access (e.g., there is no macro-generated
305     /// `get_socket_<name>()` function).
306     const Property<std::string>& getConnecteePathProp() const;
307     /// Writable access to the connectee path property from the Component in
308     /// which this Socket resides. Calling this will mark the Component as
309     /// not "up to date with properties"
310     /// (Object::isObjectUpToDateWithProperties()).
311     Property<std::string>& updConnecteePathProp();
312 
313 
314 private:
315 
316     /// Prepend the provided name to all (if any) absolute connectee paths
317     /// stored in this socket. This is to be used when adding one component
318     /// to another.
319     virtual void prependComponentPathToConnecteePath(
320             const std::string& pathToPrepend);
321 
322     std::string _name;
323     SimTK::Stage _connectAtStage = SimTK::Stage::Empty;
324     PropertyIndex _connecteePathIndex;
325     // Even though updConnecteePathProp() requires non-const access to this
326     // pointer, we make this a const pointer to reduce the chance of mis-use.
327     // If this were a non-const pointer, then const functions in this class
328     // would be able to edit _owner (see declaration of ReferencePtr).
329     SimTK::ReferencePtr<const Component> _owner;
330     // _isList must be after _owner, as _owner is used to set its value.
331     bool _isList;
332 
333     /* So that Component can invoke setOwner(), etc. */
334     friend Component;
335 
336 //=============================================================================
337 };  // END class AbstractSocket
338 
339 
340 template<class T>
341 class Socket : public AbstractSocket {
342 public:
343 
344     // default copy constructor
345 
~Socket()346     virtual ~Socket() {}
347 
clone()348     Socket<T>* clone() const override { return new Socket<T>(*this); }
349 
350     /** Is the Socket connected to object of type T? */
isConnected()351     bool isConnected() const override {
352         return !connectee.empty();
353     }
354 
getConnecteeAsObject()355     const T& getConnecteeAsObject() const override {
356         return connectee.getRef();
357     }
358 
359     /** Temporary access to the connectee for testing purposes. Real usage
360         will be through the Socket (and Input) interfaces.
361         For example, Input should short circuit to its Output's getValue()
362         once it is connected.
363     Return a const reference to the object connected to this Socket */
364     const T& getConnectee() const;
365 
366     /** Connect this Socket to the provided connectee object */
connect(const Object & object)367     void connect(const Object& object) override {
368         const T* objT = dynamic_cast<const T*>(&object);
369         if (!objT) {
370             std::stringstream msg;
371             msg << "Type mismatch: Socket '" << getName() << "' of type "
372                 << getConnecteeTypeName() << " cannot connect to '"
373                 << object.getName() << "' of type "
374                 << object.getConcreteClassName() << ".";
375             OPENSIM_THROW(Exception, msg.str());
376         }
377 
378         connectInternal(*objT);
379     }
380 
381     /** Find a component using a partial path or component name of the correct
382      * type anywhere in the model (using Component::findComponent()) and
383      * connect to that component.
384      *
385      * Throws an exception If you provide only a component name and the
386      * model has multiple components with that nume.
387      * */
388     void findAndConnect(const ComponentPath& connectee) override;
389 
390     /** Connect this Socket given its connectee path property  */
391     void finalizeConnection(const Component& root) override;
392 
disconnect()393     void disconnect() override {
394         connectee.reset(nullptr);
395     }
396 
397     /** Derived classes must satisfy this Interface */
398     /** get the type of object this socket connects to*/
getConnecteeTypeName()399     std::string getConnecteeTypeName() const override {
400         return T::getClassName();
401     }
402 
403     SimTK_DOWNCAST(Socket, AbstractSocket);
404 
405     /** For use in python/java/MATLAB bindings. */
406     // This method exists for consistency with Object's safeDownCast.
safeDownCast(AbstractSocket * base)407     static Socket<T>* safeDownCast(AbstractSocket* base) {
408         return dynamic_cast<Socket<T>*>(base);
409     }
410 
411 protected:
412     /** Create a Socket that can only connect to Object of type T with
413     specified name and stage at which it should be connected. Only Component
414     can ever construct this class.
415     @param name               name of the socket used to describe its dependency.
416     @param connecteePathIndex Index of the property in the containing Component
417                               that holds this Socket's connectee path(s).
418     @param connectAtStage     Stage at which Socket should be connected.
419     @param owner              The component that contains this input. */
Socket(const std::string & name,const PropertyIndex & connecteePathIndex,const SimTK::Stage & connectAtStage,Component & owner)420     Socket(const std::string& name,
421            const PropertyIndex& connecteePathIndex,
422            const SimTK::Stage& connectAtStage,
423            Component& owner) :
424         AbstractSocket(name, connecteePathIndex, connectAtStage, owner),
425         connectee(nullptr) {}
426 
427     /** So that Component can construct a Socket. */
428     friend Component;
429 
430 private:
431 
connectInternal(const T & objT)432     void connectInternal(const T& objT) {
433         connectee = &objT;
434     }
435 
436     mutable SimTK::ReferencePtr<const T> connectee;
437 }; // END class Socket<T>
438 
439 
440 /** A specialized Socket that connects to an Output signal is an Input.
441 An AbstractInput enables maintenance of a list of unconnected Inputs.
442 An Input can either be a single-value Input or a list Input. A list Input
443 can connect to multiple (Output) Channels.
444 
445 #### XML Syntax of a connectee path
446 
447 For every %Input that a component has, the XML representation of the component
448 contains an element named `input_<input_name>`. For example, a component
449 that has an Input named `desired_angle` might look like the following in XML:
450 @code
451     <MyComponent name="my_comp">
452         <input_desired_angle>
453             ../foo/angle
454         </input_desired_angle>
455         ...
456     </MyComponent>
457 @endcode
458 You use this field to specify the outputs/channels that should be connected to
459 this input (that is, the connectees). The syntax for the connectee path
460 property is as follows:
461 @code
462 <path/to/component>|<output_name>[:<channel_name>][(<alias>)]
463 @endcode
464 Angle brackets indicate fields that one would fill in, and square brackets
465 indicate optional fields. The `<path/to/component>` can be relative or
466 absolute, and describes the location of the Component that contains the
467 desired Output relative to the location of the Component that contains this
468 Input. The `<path/to/component>` and `<output_name>` must always be specified.
469 The `<channel_name>` should only be specified if the %Output is a list output
470 (i.e., it has multiple channels). The `<alias>` is a name for the
471 output/channel that is specific to this input, and it is optional.
472 All fields should contain only letters, numbers, and underscores (the path
473 to the component can contain slashes and periods); fields must *not* contain
474 spaces.
475 Here are some examples:
476  - `../marker_data|column:left_ankle`: The TableSourceVec3 component
477    `../marker_data` has a list output `column`, and we want to connect to its
478    `left_ankle` channel.
479  - `../averager|output(knee_joint_center)`: The component `../averager`
480    (presumably a component that averages its inputs) has an output named
481    `output`, and we are aliasing this output as `knee_joint_center`.
482  - `/leg_model/soleus|activation`: This connectee path uses the absolute path
483    to component `soleus`, which has an output named `activation`.
484 
485 List inputs can contain multiple entries in its connectee path, with the
486 entries separated by a space. For example:
487 @code
488 <input_experimental_markers>
489     ../marker_data|column:left_ankle ../marker_data|column:right_ankle ../averager|output(knee_joint_center)
490 </input_experimental_markers>
491 @endcode
492 */
493 class OSIMCOMMON_API AbstractInput : public AbstractSocket {
494 public:
495 
~AbstractInput()496     virtual ~AbstractInput() {}
497 
498     // Change the return type of clone(). This is similar to what the Object
499     // macros do (see OpenSim_OBJECT_ABSTRACT_DEFS).
500     AbstractInput* clone() const override = 0;
501 
502     // Socket interface
connect(const Object & object)503     void connect(const Object& object) override {
504         std::stringstream msg;
505         msg << "Input::connect(): ERR- Cannot connect '" << object.getName()
506             << "' of type " << object.getConcreteClassName() <<
507             ". Input can only connect to an Output.";
508         throw Exception(msg.str(), __FILE__, __LINE__);
509     }
510 
511     /** TODO */
512 //    virtual bool isConnecteeSpecified() const = 0;
513 
514     /** Connect this Input to a single-valued (single-channel) Output or, if
515     this is a list %Input and the %Output is a list %Output, connect to all the
516     channels of the %Output. You can optionally provide an alias that will be
517     used by the Component owning this %Input to refer to the %Output. If this
518     method connects to multiple channels, the alias will be used for all
519     channels. */
520     virtual void connect(const AbstractOutput& output,
521                          const std::string& alias = "") = 0;
522     /** Connect this Input to a single output channel. This
523     method can be used with both single-valued and list %Inputs. You can
524     optionally provide an alias that will be used by the Component owning this
525     %Input to refer to the %Channel. */
526     virtual void connect(const AbstractChannel& channel,
527                          const std::string& alias = "") = 0;
528 
529     /** Get the alias for a Channel. An alias is a description for a %Channel
530     that is specific to how the Input will use the %Channel. For example, the
531     Component that owns this %Input might expect the aliases to be the names of
532     markers in the model. This method can be used only for non-list %Inputs; for
533     list %Inputs, use the overload that takes an index.
534     You must finalize connections (Component::finalizeConnections()) first. */
535     virtual const std::string& getAlias() const = 0;
536 
537     /** Get the alias for the Channel indicated by the provided index. An alias
538     is a description for a %Channel that is specific to how the Input will use
539     the %Channel. For example, the Component that owns this %Input might expect
540     the aliases to be the names of markers in the model.
541     You must finalize connections (Component::finalizeConnections()) first. */
542     virtual const std::string& getAlias(unsigned index) const = 0;
543 
544     /** %Set the alias for a Channel. If this is a list Input, the aliases of all
545     %Channels will be set to the provided string. If you wish to set the alias
546     of only one %Channel, use the two-argument overload.
547     You must finalize connections (Component::finalizeConnections()) first. */
548     virtual void setAlias(const std::string& alias) = 0;
549 
550     /** %Set the alias for the Channel indicated by the provided index.
551     You must finalize connections (Component::finalizeConnections()) first. */
552     virtual void setAlias(unsigned index, const std::string& alias) = 0;
553 
554     /** Get the label for this Channel. If an alias has been set, the label is
555     the alias; otherwise, the label is the full path of the Output that has been
556     connected to this Input. This method can be used only for non-list %Inputs;
557     for list %Inputs, use the single-argument overload.
558     You must finalize connections (Component::finalizeConnections()) first. */
559     virtual std::string getLabel() const = 0;
560 
561     /** Get the label for the Channel indicated by the provided index. If an
562     alias has been set, the label is the alias; otherwise, the label is the full
563     path of the %Channel that has been connected to this Input.
564     You must finalize connections (Component::finalizeConnections()) first. */
565     virtual std::string getLabel(unsigned index) const = 0;
566 
567     /** Break up a connectee path into its output path, channel name
568      (empty for single-value outputs), and alias. This function writes
569      to the passed-in outputPath, channelName, and alias.
570 
571      Examples:
572      @verbatim
573      /foo/bar|output
574      outputPath is "/foo/bar/output"
575      channelName is ""
576      alias is ""
577 
578      /foo/bar|output:channel
579      outputPath is "/foo/bar/output"
580      channelName is "channel"
581      alias is ""
582 
583      /foo/bar|output(baz)
584      outputPath is "/foo/bar/output"
585      channelName is ""
586      alias is "baz"
587 
588      /foo/bar|output:channel(baz)
589      outputPath is "/foo/bar|output"
590      channelName is "channel"
591      alias is "baz"
592      @endverbatim
593      */
parseConnecteePath(const std::string & connecteePath,std::string & componentPath,std::string & outputName,std::string & channelName,std::string & alias)594     static bool parseConnecteePath(const std::string& connecteePath,
595                                    std::string& componentPath,
596                                    std::string& outputName,
597                                    std::string& channelName,
598                                    std::string& alias) {
599         auto bar = connecteePath.rfind("|");
600         auto colon = connecteePath.rfind(":");
601         auto leftParen = connecteePath.rfind("(");
602         auto rightParen = connecteePath.rfind(")");
603 
604         componentPath = connecteePath.substr(0, bar);
605         outputName = connecteePath.substr(bar + 1,
606                                           std::min(colon, leftParen) - (bar + 1));
607 
608         // Channel name.
609         if (colon != std::string::npos) {
610             channelName = connecteePath.substr(colon + 1, leftParen - (colon + 1));
611         } else {
612             channelName = "";
613         }
614 
615         // Alias.
616         if (leftParen != std::string::npos && rightParen != std::string::npos) {
617             alias = connecteePath.substr(leftParen + 1,
618                                          rightParen - (leftParen + 1));
619         } else {
620             alias = "";
621         }
622 
623         return true;
624     }
625 
626     /** Compose the connectee path from its constituents. This is the opposite
627     operation of parseConnecteePath().
628     Example:
629     @verbatim
630      if inputs are
631        componentPath --> "/foo/bar"
632        outputName    --> "output"
633        channelName   --> "channel"
634        alias         --> "baz"
635      then result --> /foo/bar|output:channel(baz)
636     @endverbatim
637     */
composeConnecteePath(const std::string & componentPath,const std::string & outputName,const std::string & channelName,const std::string & alias)638     static std::string composeConnecteePath(const std::string& componentPath,
639                                             const std::string& outputName,
640                                             const std::string& channelName,
641                                             const std::string& alias) {
642         auto path = componentPath;
643         path += "|";
644         path += outputName;
645         if(!channelName.empty())
646             path += ":" + channelName;
647         if(!alias.empty())
648             path += "(" + alias + ")";
649 
650         return path;
651     }
652 
653 protected:
654     /** Create an AbstractInput (Socket) that connects only to an
655     AbstractOutput specified by name and stage at which it should be connected.
656     Only Component should ever construct this class.
657     @param name              name of the dependent (Abstract)Output.
658     @param connecteePathIndex Index of the property in the containing Component
659                               that holds this Input's connectee path(s).
660     @param connectAtStage     Stage at which Input should be connected.
661     @param owner              The component that contains this input. */
AbstractInput(const std::string & name,const PropertyIndex & connecteePathIndex,const SimTK::Stage & connectAtStage,Component & owner)662     AbstractInput(const std::string& name,
663                   const PropertyIndex& connecteePathIndex,
664                   const SimTK::Stage& connectAtStage,
665                   Component& owner) :
666         AbstractSocket(name, connecteePathIndex, connectAtStage, owner) {}
667 
668 private:
669     void prependComponentPathToConnecteePath(
670             const std::string& pathToPrepend) override;
671 
672     /* So that Component can invoke prependComponentPathToConnecteePath(). */
673     friend Component;
674 
675 //=============================================================================
676 };  // END class AbstractInput
677 
678 
679 /** An Input<Y> must be connected by an Output<Y> */
680 template<class T>
681 class Input : public AbstractInput {
682 public:
683 
684     typedef typename Output<T>::Channel Channel;
685 
686     typedef std::vector<SimTK::ReferencePtr<const Channel>> ChannelList;
687     typedef std::vector<std::string> AliasList;
688 
clone()689     Input<T>* clone() const override { return new Input<T>(*this); }
690 
691     /** Connect this Input to the provided (Abstract)Output. */
692     // Definition is in Component.h
693     void connect(const AbstractOutput& output,
694                  const std::string& alias = "") override;
695 
696     void connect(const AbstractChannel& channel,
697                  const std::string& alias = "") override;
698 
699     /** Connect this Input given a root Component to search for
700     the Output according to the connectee path of this Input  */
701     void finalizeConnection(const Component& root) override;
702 
disconnect()703     void disconnect() override {
704         _registeredChannels.clear();
705         _connectees.clear();
706         _aliases.clear();
707     }
708 
isConnected()709     bool isConnected() const override {
710         return _connectees.size() == getNumConnectees();
711     }
712 
713     /** Get the value of this Input when it is connected. Redirects to connected
714     Output<T>'s getValue() with minimal overhead. This method can be used only
715     for non-list Input(s). For list Input(s), use the other overload.         */
getValue(const SimTK::State & state)716     const T& getValue(const SimTK::State &state) const {
717         OPENSIM_THROW_IF(isListSocket(),
718                          Exception,
719                          "Input<T>::getValue(): an index must be "
720                          "provided for a list input.");
721 
722         return getValue(state, 0);
723     }
724 
725     /**Get the value of this Input when it is connected. Redirects to connected
726     Output<T>'s getValue() with minimal overhead. Specify the index of the
727     Channel whose value is desired.                                           */
getValue(const SimTK::State & state,unsigned index)728     const T& getValue(const SimTK::State &state, unsigned index) const {
729         OPENSIM_THROW_IF(!isConnected(), InputNotConnected, getName());
730         using SimTK::isIndexInRange;
731         SimTK_INDEXCHECK(index, getNumConnectees(),
732                          "Input<T>::getValue()");
733 
734         return _connectees[index].getRef().getValue(state);
735     }
736 
737     /** Get the Channel associated with this Input. This method can only be
738     used for non-list Input(s). For list Input(s), use the other overload.    */
getChannel()739     const Channel& getChannel() const {
740         OPENSIM_THROW_IF(isListSocket(),
741                          Exception,
742                          "Input<T>::getChannel(): an index must be "
743                          "provided for a list input.");
744 
745         return getChannel(0);
746     }
747 
748     /** Get the Channel associated with this Input. Specify the index of the
749     channel desired.                                                          */
getChannel(unsigned index)750     const Channel& getChannel(unsigned index) const {
751         OPENSIM_THROW_IF(!isConnected(), InputNotConnected, getName());
752         using SimTK::isIndexInRange;
753         SimTK_INDEXCHECK_ALWAYS(index, getNumConnectees(),
754                                 "Input<T>::getChannel()");
755         SimTK_ASSERT(index < _connectees.size(), "Internal error: "
756             "getNumConnectees() and _connectees.size() are not consistent.");
757         return _connectees[index].getRef();
758     }
759 
getAlias()760     const std::string& getAlias() const override {
761         OPENSIM_THROW_IF(isListSocket(),
762                          Exception,
763                          "Input<T>::getAlias(): this is a list Input; an index "
764                          "must be provided.");
765 
766         return getAlias(0);
767     }
768 
getAlias(unsigned index)769     const std::string& getAlias(unsigned index) const override {
770         OPENSIM_THROW_IF(!isConnected(), InputNotConnected, getName());
771         using SimTK::isIndexInRange;
772         SimTK_INDEXCHECK_ALWAYS(index, getNumConnectees(),
773                                 "Input<T>::getAlias()");
774         SimTK_ASSERT(index < _aliases.size(), "Internal error: "
775             "getNumConnectees() and _aliases.size() are not consistent.");
776         return _aliases[index];
777     }
778 
setAlias(const std::string & alias)779     void setAlias(const std::string& alias) override {
780         for (unsigned i=0; i<getNumConnectees(); ++i)
781             setAlias(i, alias);
782     }
783 
setAlias(unsigned index,const std::string & alias)784     void setAlias(unsigned index, const std::string& alias) override {
785         OPENSIM_THROW_IF(!isConnected(), InputNotConnected, getName());
786         using SimTK::isIndexInRange;
787         SimTK_INDEXCHECK_ALWAYS(index, getNumConnectees(),
788                                 "Input<T>::setAlias()");
789 
790         const auto& connecteePath = getConnecteePath(index);
791         std::string componentPath{};
792         std::string outputName{};
793         std::string channelName{};
794         std::string currAlias{};
795         parseConnecteePath(connecteePath,
796                            componentPath,
797                            outputName,
798                            channelName,
799                            currAlias);
800         updConnecteePathProp().setValue(index,
801                                         composeConnecteePath(componentPath,
802                                                              outputName,
803                                                              channelName,
804                                                              alias));
805 
806         _aliases[index] = alias;
807     }
808 
getLabel()809     std::string getLabel() const override {
810         OPENSIM_THROW_IF(!isConnected(),
811                          InputNotConnected, getName());
812         OPENSIM_THROW_IF(isListSocket(),
813                          Exception,
814                          "Input<T>::getLabel(): this is a list Input; an index "
815                          "must be provided.");
816 
817         return getLabel(0);
818     }
819 
getLabel(unsigned index)820     std::string getLabel(unsigned index) const override {
821         OPENSIM_THROW_IF(!isConnected(),
822                          InputNotConnected, getName());
823         using SimTK::isIndexInRange;
824         SimTK_INDEXCHECK_ALWAYS(index, getNumConnectees(),
825                                 "Input<T>::getLabel()");
826 
827         const std::string alias = getAlias(index);
828         if (!alias.empty())
829             return alias;
830 
831         return getChannel(index).getPathName();
832     }
833 
834     /** Access the values of all the channels connected to this Input as a
835     SimTK::Vector_<T>. The elements are in the same order as the channels.
836     */
getVector(const SimTK::State & state)837     SimTK::Vector_<T> getVector(const SimTK::State& state) const {
838         SimTK::Vector_<T> v(static_cast<int>(_connectees.size()));
839         for (unsigned ichan = 0u; ichan < _connectees.size(); ++ichan) {
840             v[ichan] = _connectees[ichan]->getValue(state);
841         }
842         return v;
843     }
844 
845     /** Get const access to the channels connected to this input.
846         You can use this to iterate through the channels.
847         @code{.cpp}
848         for (const auto& chan : getChannels()) {
849             std::cout << chan.getValue(state) << std::endl;
850         }
851         @endcode
852     */
getChannels()853     const ChannelList& getChannels() const {
854         return _connectees;
855     }
856 
857     /** Return the typename of the Output value, T, that satisfies
858         this Input<T>. No reason to return Output<T> since it is a
859         given that only an Output can satisfy an Input. */
getConnecteeTypeName()860     std::string getConnecteeTypeName() const override {
861         return SimTK::NiceTypeName<T>::namestr();
862     }
863 
864     SimTK_DOWNCAST(Input, AbstractInput);
865 
866     /** For use in python/java/MATLAB bindings. */
867     // This method exists for consistency with Object's safeDownCast.
safeDownCast(AbstractInput * base)868     static Input<T>* safeDownCast(AbstractInput* base) {
869         return dynamic_cast<Input<T>*>(base);
870     }
871 
872 protected:
873     /** Create an Input<T> (Socket) that can only connect to an Output<T>
874     name and stage at which it should be connected. Only Component should ever
875     construct an Input.
876     @param name               name of the Output dependency.
877     @param connecteePathIndex Index of the property in the containing Component
878                               that holds this Input's connectee path(s).
879     @param connectAtStage     Stage at which Input should be connected.
880     @param owner              The component that contains this input. */
Input(const std::string & name,const PropertyIndex & connecteePathIndex,const SimTK::Stage & connectAtStage,Component & owner)881     Input(const std::string& name, const PropertyIndex& connecteePathIndex,
882           const SimTK::Stage& connectAtStage, Component& owner) :
883         AbstractInput(name, connecteePathIndex, connectAtStage, owner) {}
884 
885     /** So that Component can construct an Input. */
886     friend Component;
887 
888 private:
889     /** Register a channel to connect to. Since channels are created on the fly,
890      * we do not want to hold onto references to channels. Instead, we hold
891      * onto references of outputs. */
892     void registerChannel(const AbstractChannel& channel,
893             const std::string& alias, bool validate = true)
894     {
895         const Channel* chanT = nullptr;
896         if (validate) {
897             chanT = dynamic_cast<const Channel*>(&channel);
898             if (!chanT) {
899                 std::stringstream msg;
900                 msg << "Type mismatch between Input and Output: Input '"
901                     << getName() << "' of type " << getConnecteeTypeName()
902                     << " cannot connect to Output (channel) '"
903                     << channel.getPathName()
904                     << "' of type " << channel.getTypeName() << ".";
905                 OPENSIM_THROW(Exception, msg.str());
906             }
907         } else {
908             chanT = static_cast<const Channel*>(&channel);
909         }
910         _registeredChannels.emplace_back(
911                 SimTK::ReferencePtr<const Output<T>>(&chanT->getOutput()),
912                 chanT->getChannelName(), alias);
913     }
connectInternal(const AbstractChannel & channel,const std::string & alias)914     void connectInternal(const AbstractChannel& channel,
915             const std::string& alias) {
916         const auto* chanT = dynamic_cast<const Channel*>(&channel);
917         if (!chanT) {
918             std::stringstream msg;
919             msg << "Type mismatch between Input and Output: Input '"
920                 << getName() << "' of type " << getConnecteeTypeName()
921                 << " cannot connect to Output (channel) '"
922                 << channel.getPathName()
923                 << "' of type " << channel.getTypeName() << ".";
924             OPENSIM_THROW(Exception, msg.str());
925         }
926 
927         if (!isListSocket()) {
928             _connectees.clear();
929             _aliases.clear();
930         }
931         _connectees.emplace_back(chanT);
932         _aliases.push_back(alias);
933     }
934     // These are channels the user has requested that we connect to.
935     using RegisteredChannel =
936             std::tuple<SimTK::ReferencePtr<const Output<T>>,
937                     std::string, std::string>;
938     SimTK::ResetOnCopy<std::vector<RegisteredChannel>> _registeredChannels;
939     SimTK::ResetOnCopy<ChannelList> _connectees;
940     // Aliases are serialized, since tools may depend on them for
941     // interpreting the connected channels.
942     SimTK::ResetOnCopy<AliasList> _aliases;
943 }; // END class Input<Y>
944 
945 
946 /// @name Creating Sockets to other objects for your Component
947 /// Use these macros at the top of your component class declaration,
948 /// near where you declare @ref Property properties.
949 /// @{
950 /** Create a socket for this component's dependence on another component.
951  * You must specify the type of the component that can be connected to this
952  * socket. The comment should describe how the connected component
953  * (connectee) is used by this component.
954  *
955  * Here's an example for using this macro:
956  * @code{.cpp}
957  * #include <OpenSim/Simulation/Model/PhysicalOffsetFrame.h>
958  * class MyComponent : public Component {
959  * public:
960  *     OpenSim_DECLARE_SOCKET(parent, PhysicalOffsetFrame,
961  *             "To locate this component.");
962  *     ...
963  * };
964  * @endcode
965  *
966  * @note This macro requires that you have included the header that defines
967  * type `T`, as shown in the example above. We currently do not support
968  * declaring sockets if `T` is only forward-declared.
969  *
970  * @note If you use this macro in your class, then you should *NOT* implement
971  * a custom copy constructor---try to use the default one. The socket will
972  * not get copied properly if you create a custom copy constructor.
973  * We may add support for custom copy constructors with Sockets in the
974  * future.
975  *
976  * @see Component::constructSocket()
977  * @relates OpenSim::Socket */
978 #define OpenSim_DECLARE_SOCKET(cname, T, comment)                           \
979     /** @name Sockets                                                    */ \
980     /** @{                                                               */ \
981     /** comment                                                          */ \
982     /** In an XML file, you can set this Socket's connectee path         */ \
983     /** via the <b>\<socket_##cname\></b> element.                       */ \
984     /** This socket was generated with the                               */ \
985     /** #OpenSim_DECLARE_SOCKET macro;                                   */ \
986     /** see AbstractSocket for more information.                         */ \
987     /** @see connectSocket_##cname##()                                   */ \
988     OpenSim_DOXYGEN_Q_PROPERTY(T, cname)                                    \
989     /** @}                                                               */ \
990     /** @cond                                                            */ \
991     PropertyIndex PropertyIndex_socket_##cname {                            \
992         this->template constructSocket<T>(#cname,                           \
993                 "Path to a Component that satisfies the Socket '"           \
994                 #cname "' of type " #T " (description: " comment ").")      \
995     };                                                                      \
996     /** @endcond                                                         */ \
997     /** @name Socket-related functions                                   */ \
998     /** @{                                                               */ \
999     /** Connect the '##cname##' Socket to an object of type T##.         */ \
1000     /** Call finalizeConnections() afterwards to update the socket's     */ \
1001     /** connectee path property. The reference to the connectee set here */ \
1002     /** takes precedence over the connectee path property.               */ \
1003     void connectSocket_##cname(const Object& object) {                      \
1004         this->updSocket(#cname).connect(object);                            \
1005     }                                                                       \
1006     /** @}                                                               */
1007 
1008 // The following doxygen-like description does NOT actually appear in doxygen.
1009 /* Preferably, use the #OpenSim_DECLARE_SOCKET macro. Only use this macro
1010  * when are you unable to include the header that defines type `T`. This might
1011  * be the case if you have a circular dependency between your class and `T`.
1012  * In such cases, you must:
1013  *
1014  *  -# forward-declare type `T`
1015  *  -# call this macro inside the definition of your class, and
1016  *  -# call #OpenSim_DEFINE_SOCKET_FD in your class's .cpp file (notice the
1017  *      difference: DEFINE vs DECLARE).
1018  *
1019  * MyComponent.h:
1020  * @code{.cpp}
1021  * namespace OpenSim {
1022  * class PhysicalOffsetFrame;
1023  * class MyComponent : public Component {
1024  * OpenSim_DECLARE_CONCRETE_OBJECT(MyComponent, Component);
1025  * public:
1026  *     OpenSim_DECLARE_SOCKET_FD(parent, PhysicalOffsetFrame,
1027  *             "To locate this component.");
1028  *     ...
1029  * };
1030  * }
1031  * @endcode
1032  *
1033  * MyComponent.cpp:
1034  * @code{.cpp}
1035  * #include "MyComponent.h"
1036  * OpenSim_DEFINE_SOCKET_FD(parent, OpenSim::MyComponent);
1037  * ...
1038  * @endcode
1039  *
1040  * You can also look at the OpenSim::Geometry source code for an example.
1041  *
1042  * @note Do NOT forget to call OpenSim_DEFINE_SOCKET_FD in your .cpp file!
1043  *
1044  * The "FD" in the name of this macro stands for "forward-declared."
1045  *
1046  * @warning This macro is experimental and may be removed in future versions.
1047  *
1048  *
1049  * @note If you use this macro in your class, then you should *NOT* implement
1050  * a custom copy constructor---try to use the default one. The Socket will
1051  * not get copied properly if you create a custom copy constructor.
1052  * We may add support for custom copy constructors with Sockets in the
1053  * future.
1054  *
1055  * @see Component::constructSocket()
1056  * @relates OpenSim::Socket */
1057 #define OpenSim_DECLARE_SOCKET_FD(cname, T, comment)                        \
1058     /** @name Sockets                                                    */ \
1059     /** @{                                                               */ \
1060     /** comment                                                          */ \
1061     /** In an XML file, you can set this socket's connectee path         */ \
1062     /** via the <b>\<socket_##cname\></b> element.                       */ \
1063     /** See AbstractSocket for more information.                         */ \
1064     /** @see connectsocket_##cname##()                                   */ \
1065     OpenSim_DOXYGEN_Q_PROPERTY(T, cname)                                    \
1066     /** @}                                                               */ \
1067     /** @cond                                                            */ \
1068     PropertyIndex PropertyIndex_socket_##cname {           \
1069         constructSocket_##cname()                                           \
1070     };                                                                      \
1071     /* Declare the method used in the in-class member initializer.       */ \
1072     /* This method will be defined by OpenSim_DEFINE_SOCKET_FD.          */ \
1073     PropertyIndex constructSocket_##cname();                                \
1074     /* Remember the provided type so we can use it in the DEFINE macro.  */ \
1075     typedef T _socket_##cname##_type;                                       \
1076     /** @endcond                                                         */ \
1077     /** @name Socket-related functions                                   */ \
1078     /** @{                                                               */ \
1079     /** Connect the '##cname##' Socket to an object of type T##.         */ \
1080     /** Call finalizeConnections() afterwards to update the socket's     */ \
1081     /** connectee path property. The reference to the connectee set here */ \
1082     /** takes precedence over the connectee path property.               */ \
1083     void connectSocket_##cname(const Object& object) {                      \
1084         this->updSocket(#cname).connect(object);                            \
1085     }                                                                       \
1086     /** @}                                                               */
1087 
1088 // The following doxygen-like description does NOT actually appear in doxygen.
1089 /* When specifying a Socket to a forward-declared type (using
1090  * OpenSim_DECLARE_SOCKET_FD in the class definition), you must call this
1091  * macro in your .cpp file.  The arguments are the name of the socket (the
1092  * same one provided to OpenSim_DECLARE_SOCKET_FD) and the class in which
1093  * the socket exists. See #OpenSim_DECLARE_SOCKET_FD for an example.
1094  *
1095  * @warning This macro is experimental and may be removed in future versions.
1096  *
1097  * @see #OpenSim_DECLARE_SOCKET_FD
1098  * @relates OpenSim::Socket */
1099 // This macro defines the method that the in-class member initializer calls
1100 // to construct the Socket. The reason why this must be in the .cpp file is
1101 // that putting the template member function `template <typename T>
1102 // Component::constructSocket` in the header requires that `T` is not an
1103 // incomplete type (specifically, when compiling cpp files for classes OTHER
1104 // than `MyComponent` but that include MyComponent.h). OpenSim::Geometry is an
1105 // example of this scenario.
1106 #define OpenSim_DEFINE_SOCKET_FD(cname, Class)                              \
1107 PropertyIndex Class::constructSocket_##cname() {                            \
1108     using T = _socket_##cname##_type;                                       \
1109     std::string typeStr = T::getClassName();                                \
1110     return this->template constructSocket<T>(#cname,                        \
1111         "Path to a Component that satisfies the Socket '"                   \
1112         #cname "' of type " + typeStr + ".");                               \
1113 }
1114 /// @}
1115 
1116 /// @name Creating Inputs for your Component
1117 /// Use these macros at the top of your component class declaration,
1118 /// near where you declare @ref Property properties.
1119 /// @{
1120 /** Create a socket for this component's dependence on an output signal from
1121  *  another component. It is a placeholder for an Output that can be connected
1122  *  to it. An output must have the same type T as an input to be connected
1123  *  to it. You must also specify the stage at which you require this input
1124  *  quantity. The comment should describe how the input quantity is used.
1125  *
1126  *  An Input declared with this macro can connect to only one Output.
1127  *
1128  *  Here's an example for using this macro:
1129  *  @code{.cpp}
1130  *  class MyComponent : public Component {
1131  *  public:
1132  *      OpenSim_DECLARE_INPUT(emg, double, SimTK::Stage::Velocity, "For validation.");
1133  *      ...
1134  *  };
1135  *  @endcode
1136  *
1137  * @note If you use this macro in your class, then you should *NOT* implement
1138  * a custom copy constructor---try to use the default one. The Input will
1139  * not get copied properly if you create a custom copy constructor.
1140  * We may add support for custom copy constructors with Inputs in the future.
1141  *
1142  * @see Component::constructInput()
1143  * @relates OpenSim::Input */
1144 #define OpenSim_DECLARE_INPUT(iname, T, istage, comment)                    \
1145     /** @name Inputs                                                     */ \
1146     /** @{                                                               */ \
1147     /** comment                                                          */ \
1148     /** This input is needed at stage istage##.                          */ \
1149     /** In an XML file, you can set this Input's connectee path          */ \
1150     /** via the <b>\<input_##iname\></b> element.                        */ \
1151     /** The syntax for a connectee path is                               */ \
1152     /** `<path/to/component>|<output_name>[:<channel_name>][(<alias>)]`. */ \
1153     /** This input was generated with the                                */ \
1154     /** #OpenSim_DECLARE_INPUT macro;                                    */ \
1155     /** see AbstractInput for more information.                          */ \
1156     /** @see connectInput_##iname##()                                    */ \
1157     OpenSim_DOXYGEN_Q_PROPERTY(T, iname)                                    \
1158     /** @}                                                               */ \
1159     /** @cond                                                            */ \
1160     PropertyIndex PropertyIndex_input_##iname {                             \
1161         this->template constructInput<T>(#iname, false,                     \
1162             "Path to an output (channel) to satisfy the one-value Input '"  \
1163             #iname "' of type " #T " (description: " comment ").",  istage) \
1164     };                                                                      \
1165     /** @endcond                                                         */ \
1166     /** @name Input-related functions                                    */ \
1167     /** @{                                                               */ \
1168     /** Connect this Input to a single-valued (single-channel) Output.   */ \
1169     /** The output must be of type T##.                                  */ \
1170     /** This input is single-valued and thus cannot connect to a         */ \
1171     /** list output.                                                     */ \
1172     /** You can optionally provide an alias that will be used by this    */ \
1173     /** component to refer to the output.                                */ \
1174     /** Call finalizeConnections() afterwards to update the input's      */ \
1175     /** connectee path property. The reference to the output set here    */ \
1176     /** takes precedence over the connectee path property.               */ \
1177     void connectInput_##iname(const AbstractOutput& output,                 \
1178                               const std::string& alias = "") {              \
1179         updInput(#iname).connect(output, alias);                            \
1180     }                                                                       \
1181     /** Connect this Input to an output channel of type T##.             */ \
1182     /** You can optionally provide an alias that will be used by this    */ \
1183     /** component to refer to the channel.                               */ \
1184     /** Call finalizeConnections() afterwards to update the input's      */ \
1185     /** connectee path property. The reference to the channel set here   */ \
1186     /** takes precedence over the connectee path property.               */ \
1187     void connectInput_##iname(const AbstractChannel& channel,               \
1188                               const std::string& alias = "") {              \
1189         updInput(#iname).connect(channel, alias);                           \
1190     }                                                                       \
1191     /** @}                                                               */
1192 
1193 // TODO create new macros to handle custom copy constructors: with
1194 // constructInput_() methods, etc. NOTE: constructProperty_() must be called
1195 // first within these macros, b/c the connectee path property must exist before
1196 // the Input etc is constructed.
1197 
1198 
1199 /** Create a list input, which can connect to more than one Channel. This
1200  * makes sense for components like reporters that can handle a flexible
1201  * number of input values.
1202  *
1203  * @note If you use this macro in your class, then you should *NOT* implement
1204  * a custom copy constructor---try to use the default one. The Input will
1205  * not get copied properly if you create a custom copy constructor.
1206  * We may add support for custom copy constructors with Inputs in the future.
1207  *
1208  * @see Component::constructInput()
1209  * @relates OpenSim::Input */
1210 #define OpenSim_DECLARE_LIST_INPUT(iname, T, istage, comment)               \
1211     /** @name Inputs (list)                                              */ \
1212     /** @{                                                               */ \
1213     /** comment                                                          */ \
1214     /** This input can connect to multiple outputs, all of which are     */ \
1215     /** needed at stage istage##.                                        */ \
1216     /** In an XML file, you can set this Input's connectee path          */ \
1217     /** via the <b>\<input_##iname\></b> element.                        */ \
1218     /** The syntax for a connectee path is                               */ \
1219     /** `<path/to/component>|<output_name>[:<channel_name>][(<alias>)]`. */ \
1220     /** This input was generated with the                                */ \
1221     /** #OpenSim_DECLARE_LIST_INPUT macro;                               */ \
1222     /** see AbstractInput for more information.                          */ \
1223     /** @see connectInput_##iname##()                                    */ \
1224     OpenSim_DOXYGEN_Q_PROPERTY(T, iname)                                    \
1225     /** @}                                                               */ \
1226     /** @cond                                                            */ \
1227     PropertyIndex PropertyIndex_input_##iname {                             \
1228         this->template constructInput<T>(#iname, true,                      \
1229             "Paths to outputs (channels) to satisfy the list Input '"       \
1230             #iname "' of type " #T " (description: " comment "). "          \
1231             "To specify multiple paths, put spaces between them.", istage)  \
1232     };                                                                      \
1233     /** @endcond                                                         */ \
1234     /** @name Input-related functions                                    */ \
1235     /** @{                                                               */ \
1236     /** Connect this Input to a single-valued or list Output.            */ \
1237     /** The output must be of type T##.                                  */ \
1238     /** If the output is a list output, this connects to all of the      */ \
1239     /** channels of the output.                                          */ \
1240     /** You can optionally provide an alias that will be used by this    */ \
1241     /** component to refer to the output; the alias will be used for all */ \
1242     /** channels of the output.                                          */ \
1243     /** Call finalizeConnections() afterwards to update the input's      */ \
1244     /** connectee path property. The reference to the output set here    */ \
1245     /** takes precedence over the connectee path property.               */ \
1246     void connectInput_##iname(const AbstractOutput& output,                 \
1247                               const std::string& alias = "") {              \
1248         updInput(#iname).connect(output, alias);                            \
1249     }                                                                       \
1250     /** Connect this Input to an output channel of type T##.             */ \
1251     /** You can optionally provide an alias that will be used by this    */ \
1252     /** component to refer to the channel.                               */ \
1253     /** Call finalizeConnections() afterwards to update the input's      */ \
1254     /** connectee path property. The reference to the channel set here   */ \
1255     /** takes precedence over the connectee path property.               */ \
1256     void connectInput_##iname(const AbstractChannel& channel,               \
1257                               const std::string& alias = "") {              \
1258         updInput(#iname).connect(channel, alias);                           \
1259     }                                                                       \
1260     /** @}                                                               */
1261 /// @}
1262 
1263 } // end of namespace OpenSim
1264 
1265 #endif  // OPENSIM_COMPONENT_SOCKET_H_
1266