Overte C++ Documentation
AssociatedTraitValues.h
1 //
2 // AssociatedTraitValues.h
3 // libraries/avatars/src
4 //
5 // Created by Stephen Birarda on 8/8/18.
6 // Copyright 2018 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_AssociatedTraitValues_h
13 #define hifi_AssociatedTraitValues_h
14 
15 #include "AvatarTraits.h"
16 
17 // This templated class is admittedly fairly confusing to look at. It is used
18 // to hold some associated value of type T for both simple (non-instanced) and instanced traits.
19 // Most of the complexity comes from the fact that simple and instanced trait types are
20 // handled differently. For each simple trait type there can be a value T, but for
21 // each instance of each instanced trait
22 // (keyed by a TraitInstanceID, which at the time of this writing is a UUID) there can be a value T.
23 // There are separate methods in most cases for simple traits and instanced traits
24 // because of this different behaviour. This class is not used to hold the values
25 // of the traits themselves, but instead an associated value like the latest version
26 // of each trait (see TraitVersions) or a state associated with each trait (like added/changed/deleted).
27 
28 namespace AvatarTraits {
29  template<typename T, T defaultValue>
30  class AssociatedTraitValues {
31  using SimpleTypesArray = std::array<T, NUM_SIMPLE_TRAITS>;
32  public:
33  // constructor that pre-fills _simpleTypes with the default value specified by the template
34  AssociatedTraitValues() { std::fill(_simpleTypes.begin(), _simpleTypes.end(), defaultValue); }
35 
37  void insert(TraitType type, T value) { _simpleTypes[type] = value; }
39  void erase(TraitType type) { _simpleTypes[type] = defaultValue; }
40 
42  T& getInstanceValueRef(TraitType traitType, TraitInstanceID instanceID);
43 
45  void instanceInsert(TraitType traitType, TraitInstanceID instanceID, T value);
46 
47  struct InstanceIDValuePair {
48  TraitInstanceID id;
49  T value;
50 
51  InstanceIDValuePair(TraitInstanceID id, T value) : id(id), value(value) {};
52  };
53 
54  using InstanceIDValuePairs = std::vector<InstanceIDValuePair>;
55 
57  InstanceIDValuePairs& getInstanceIDValuePairs(TraitType traitType);
58 
60  void instanceErase(TraitType traitType, TraitInstanceID instanceID);
62  void eraseAllInstances(TraitType traitType);
63 
65  T operator[](TraitType traitType) const { return _simpleTypes[traitType]; }
66  T& operator[](TraitType traitType) { return _simpleTypes[traitType]; }
67 
69  void reset() {
70  std::fill(_simpleTypes.begin(), _simpleTypes.end(), defaultValue);
71  _instancedTypes.clear();
72  }
73 
75  typename SimpleTypesArray::const_iterator simpleCBegin() const { return _simpleTypes.cbegin(); }
76  typename SimpleTypesArray::const_iterator simpleCEnd() const { return _simpleTypes.cend(); }
77 
79  typename SimpleTypesArray::iterator simpleBegin() { return _simpleTypes.begin(); }
80  typename SimpleTypesArray::iterator simpleEnd() { return _simpleTypes.end(); }
81 
82  struct TraitWithInstances {
83  TraitType traitType;
84  InstanceIDValuePairs instances;
85 
86  TraitWithInstances(TraitType traitType) : traitType(traitType) {};
87  TraitWithInstances(TraitType traitType, TraitInstanceID instanceID, T value) :
88  traitType(traitType), instances({{ instanceID, value }}) {};
89  };
90 
92  typename std::vector<TraitWithInstances>::const_iterator instancedCBegin() const { return _instancedTypes.cbegin(); }
93  typename std::vector<TraitWithInstances>::const_iterator instancedCEnd() const { return _instancedTypes.cend(); }
94 
96  typename std::vector<TraitWithInstances>::iterator instancedBegin() { return _instancedTypes.begin(); }
97  typename std::vector<TraitWithInstances>::iterator instancedEnd() { return _instancedTypes.end(); }
98 
99  private:
100  SimpleTypesArray _simpleTypes;
101 
103  typename std::vector<TraitWithInstances>::iterator instancesForTrait(TraitType traitType) {
104  return std::find_if(_instancedTypes.begin(), _instancedTypes.end(),
105  [traitType](TraitWithInstances& traitWithInstances){
106  return traitWithInstances.traitType == traitType;
107  });
108  }
109 
110  std::vector<TraitWithInstances> _instancedTypes;
111  };
112 
114  template <typename T, T defaultValue>
115  inline typename AssociatedTraitValues<T, defaultValue>::InstanceIDValuePairs&
116  AssociatedTraitValues<T, defaultValue>::getInstanceIDValuePairs(TraitType traitType) {
117  // first check if we already have some values for instances of this trait type
118  auto it = instancesForTrait(traitType);
119 
120  if (it != _instancedTypes.end()) {
121  return it->instances;
122  } else {
123  // if we didn't have any values for instances of the instanced trait type
124  // add an empty InstanceIDValuePairs object first and then return the reference to it
125  _instancedTypes.emplace_back(traitType);
126  return _instancedTypes.back().instances;
127  }
128  }
129 
130  // returns a reference to value for the given instance of the given instanced trait type
131  template <typename T, T defaultValue>
132  inline T& AssociatedTraitValues<T, defaultValue>::getInstanceValueRef(TraitType traitType, TraitInstanceID instanceID) {
133  // first check if we already have some values for instances of this trait type
134  auto it = instancesForTrait(traitType);
135 
136  if (it != _instancedTypes.end()) {
137  // grab the matching vector of instances
138  auto& instancesVector = it->instances;
139 
140  // check if we have a value for this specific instance ID
141  auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(),
142  [instanceID](InstanceIDValuePair& idValuePair){
143  return idValuePair.id == instanceID;
144  });
145  if (instanceIt != instancesVector.end()) {
146  return instanceIt->value;
147  } else {
148  // no value for this specific instance ID, insert the default value and return it
149  instancesVector.emplace_back(instanceID, defaultValue);
150  return instancesVector.back().value;
151  }
152  } else {
153  // no values for any instances of this trait type
154  // insert the default value for the specific instance for the instanced trait type
155  _instancedTypes.emplace_back(traitType, instanceID, defaultValue);
156  return _instancedTypes.back().instances.back().value;
157  }
158  }
159 
161  template <typename T, T defaultValue>
162  inline void AssociatedTraitValues<T, defaultValue>::instanceInsert(TraitType traitType, TraitInstanceID instanceID, T value) {
163  // first check if we already have some instances for this trait type
164  auto it = instancesForTrait(traitType);
165 
166  if (it != _instancedTypes.end()) {
167  // found some instances for the instanced trait type, check if our specific instance is one of them
168  auto& instancesVector = it->instances;
169  auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(),
170  [instanceID](InstanceIDValuePair& idValuePair){
171  return idValuePair.id == instanceID;
172  });
173  if (instanceIt != instancesVector.end()) {
174  // the instance already existed, update the value
175  instanceIt->value = value;
176  } else {
177  // the instance was not present, emplace the new value
178  instancesVector.emplace_back(instanceID, value);
179  }
180  } else {
181  // there were no existing instances for the given trait type
182  // setup the container for instances and insert the passed value for this instance ID
183  _instancedTypes.emplace_back(traitType, instanceID, value);
184  }
185  }
186 
188  template <typename T, T defaultValue>
189  inline void AssociatedTraitValues<T, defaultValue>::instanceErase(TraitType traitType, TraitInstanceID instanceID) {
190  // check if we have any instances at all for this instanced trait type
191  auto it = instancesForTrait(traitType);
192 
193  if (it != _instancedTypes.end()) {
194  // we have some instances, erase the value for the passed instance ID if it is present
195  auto& instancesVector = it->instances;
196  instancesVector.erase(std::remove_if(instancesVector.begin(),
197  instancesVector.end(),
198  [&instanceID](InstanceIDValuePair& idValuePair){
199  return idValuePair.id == instanceID;
200  }));
201  }
202  }
203 
204  using TraitVersions = AssociatedTraitValues<TraitVersion, DEFAULT_TRAIT_VERSION>;
205 };
206 
207 #endif // hifi_AssociatedTraitValues_h