OpenNI 1.3.2
XnHash.h
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * OpenNI 1.1 Alpha *
4 * Copyright (C) 2011 PrimeSense Ltd. *
5 * *
6 * This file is part of OpenNI. *
7 * *
8 * OpenNI is free software: you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published *
10 * by the Free Software Foundation, either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * OpenNI is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with OpenNI. If not, see <http://www.gnu.org/licenses/>. *
20 * *
21 ****************************************************************************/
22 #ifndef _XN_HASH_H
23 #define _XN_HASH_H
24 
25 //---------------------------------------------------------------------------
26 // Includes
27 //---------------------------------------------------------------------------
28 #include "XnList.h"
29 
30 //---------------------------------------------------------------------------
31 // Defines
32 //---------------------------------------------------------------------------
33 #define XN_HASH_LAST_BIN 256
34 #define XN_HASH_NUM_BINS (XN_HASH_LAST_BIN + 1)
35 //---------------------------------------------------------------------------
36 // Types
37 //---------------------------------------------------------------------------
41 typedef XnValue XnKey;
42 
46 typedef XnUInt8 XnHashValue;
47 
51 static XnHashValue XnDefaultHashFunction(const XnKey& key)
52 {
53  return (XnSizeT(key) & 0xff);
54 }
55 
59 static XnInt32 XnDefaultCompareFunction(const XnKey& key1, const XnKey& key2)
60 {
61  return XnInt32(XnSizeT(key1)-XnSizeT(key2));
62 }
63 
67 class XnHash
68 {
69 public:
74  {
75  public:
76  friend class XnHash;
77 
83  ConstIterator(const ConstIterator& other) :
85 
90  {
91  ++m_Iterator;
92 
93  while (m_Iterator == m_pHash->m_Bins[m_nCurrentBin]->end() &&
95  {
96  do
97  {
98  m_nCurrentBin++;
99  } while (m_pHash->m_Bins[m_nCurrentBin] == NULL);
101  }
102  return *this;
103  }
104 
109  {
110  XnHash::ConstIterator other(*this);
111  ++*this;
112  return other;
113  }
114 
119  {
120  --m_Iterator;
121 
122  while (m_Iterator == m_pHash->m_Bins[m_nCurrentBin]->end() &&
124  {
125  do
126  {
127  if (m_nCurrentBin == 0)
128  {
131  return *this;
132  }
133  m_nCurrentBin--;
134  } while (m_pHash->m_Bins[m_nCurrentBin] == NULL);
136  }
137  return *this;
138  }
139 
144  {
145  ConstIterator other(*this);
146  --*this;
147  return other;
148  }
149 
155  XnBool operator==(const ConstIterator& other) const
156  {
157  return m_Iterator == other.m_Iterator;
158  }
159 
165  XnBool operator!=(const ConstIterator& other) const
166  {
167  return m_Iterator != other.m_Iterator;
168  }
169 
173  const XnKey& Key() const
174  {
175  return ((XnNode*)(*m_Iterator))->Data();
176  }
177 
181  const XnValue& Value() const
182  {
183  return ((XnNode*)(*m_Iterator))->Next()->Data();
184  }
185 
190  {
191  return m_Iterator.GetNode();
192  }
193 
197  const XnNode* GetNode() const
198  {
199  return m_Iterator.GetNode();
200  }
201 
202  protected:
210  ConstIterator(const XnHash* pHash, XnUInt16 nBin, XnList::Iterator listIterator) :
211  m_pHash(pHash), m_nCurrentBin(nBin), m_Iterator(listIterator)
212  {
213  // Find the first valid
214  while (m_Iterator == m_pHash->m_Bins[m_nCurrentBin]->end() &&
216  {
217  do
218  {
219  m_nCurrentBin++;
220  } while (m_pHash->m_Bins[m_nCurrentBin] == NULL);
222  }
223  }
224 
230  ConstIterator(const XnHash* pHash) :
232 
234  const XnHash* m_pHash;
236  XnUInt16 m_nCurrentBin;
239  };
240 
244  class Iterator : public ConstIterator
245  {
246  public:
247  friend class XnHash;
248 
254  inline Iterator(const Iterator& other) : ConstIterator(other) {}
255 
259  inline Iterator& operator++()
260  {
261  ++(*(ConstIterator*)this);
262  return (*this);
263  }
267  inline Iterator operator++(int)
268  {
269  Iterator result = *this;
270  ++*this;
271  return (result);
272  }
273 
277  inline Iterator& operator--()
278  {
279  --(*(ConstIterator*)this);
280  return (*this);
281  }
285  inline Iterator operator--(int)
286  {
287  Iterator result = *this;
288  --*this;
289  return (result);
290  }
291 
295  XnKey& Key() const { return (XnKey&)ConstIterator::Key(); }
296 
300  XnValue& Value() const { return (XnValue&)ConstIterator::Value(); }
301 
302  protected:
310  Iterator(const XnHash* pHash, XnUInt16 nBin, XnList::Iterator listIterator) :
311  ConstIterator(pHash, nBin, listIterator)
312  {}
313 
319  Iterator(const XnHash* pHash) : ConstIterator(pHash) {}
320 
321  Iterator(const ConstIterator& other) : ConstIterator(other) {}
322  };
323 
324  friend class ConstIterator;
325 
326 public:
330  typedef XnHashValue (*XnHashFunction)(const XnKey& key);
334  typedef XnInt32 (*XnCompareFunction)(const XnKey& key1, const XnKey& key2);
335 
340  {
341  m_nInitStatus = Init();
342  }
343 
347  XnHash(const XnHash& other)
348  {
349  m_nInitStatus = Init();
351  {
352  m_nMinBin = other.m_nMinBin;
355  for (int i = 0; i < XN_HASH_NUM_BINS; i++)
356  {
357  if (other.m_Bins[i] != NULL)
358  {
359  m_Bins[i] = XN_NEW(XnList);
360  if (m_Bins[i] == NULL)
361  {
362  m_nInitStatus = XN_STATUS_ALLOC_FAILED;
363  return;
364  }
365  *(m_Bins[i]) = *(other.m_Bins[i]);
366  }
367  }
368  }
369  }
370 
374  virtual ~XnHash()
375  {
376  if (m_Bins != NULL)
377  {
378  for (int i = 0; i < XN_HASH_NUM_BINS; ++i)
379  {
380  XN_DELETE(m_Bins[i]);
381  }
383  }
384  }
385 
392  {
393  return m_nInitStatus;
394  }
395 
402  XnStatus Set(const XnKey& key, const XnValue& value)
403  {
404  XnHashValue HashValue = (*m_HashFunction)(key);
405 
406  // Check if key already exists
407  if (m_Bins[HashValue] != NULL)
408  {
409  Iterator hiter(this);
410  if (Find(key, HashValue, hiter) == XN_STATUS_OK)
411  {
412  // Replace value
413  hiter.Value() = value;
414  return XN_STATUS_OK;
415  }
416  }
417  else
418  {
419  // First time trying to access this bin, create it.
420  m_Bins[HashValue] = XN_NEW(XnList);
421  if (m_Bins[HashValue] == NULL)
422  {
423  return XN_STATUS_ALLOC_FAILED;
424  }
425  if (HashValue < m_nMinBin)
426  m_nMinBin = HashValue;
427  }
428 
429  // Get a new node for the key
430  XnNode* pKeyNode = XnNode::Allocate();
431  if (pKeyNode == NULL)
432  {
433  return XN_STATUS_ALLOC_FAILED;
434  }
435  pKeyNode->Data() = key;
436 
437  // Get a new node for the value
438  XnNode* pValueNode = XnNode::Allocate();
439  if (pValueNode == NULL)
440  {
441  XnNode::Deallocate(pKeyNode);
442  return XN_STATUS_ALLOC_FAILED;
443  }
444  pValueNode->Data() = value;
445 
446  // Concatenate the value node to the key node
447  pKeyNode->Next() = pValueNode;
448  pValueNode->Next() = NULL;
449 
450  // Add the 2 nodes as the value to the key's list
451  XnStatus ListStatus = m_Bins[HashValue]->AddLast(XnValue(pKeyNode));
452  if (ListStatus != XN_STATUS_OK)
453  {
454  // Add failed. return the 2 nodes to the pool
455  XnNode::Deallocate(pKeyNode);
456  XnNode::Deallocate(pValueNode);
457  return ListStatus;
458  }
459 
460  return XN_STATUS_OK;
461  }
462 
471  XnStatus Get(const XnKey& key, XnValue& value) const
472  {
473  // Check if key exists
474  Iterator hiter(this);
475  XnStatus FindStatus = Find(key, hiter);
476  if (FindStatus != XN_STATUS_OK)
477  {
478  // Key doesn't exist!
479  return FindStatus;
480  }
481  value = hiter.Value();
482 
483  return XN_STATUS_OK;
484  }
485 
494  XnStatus Remove(const XnKey& key, XnValue& value)
495  {
496  // find the entry to which the key belongs
497  Iterator hiter(this);
498 
499  XnStatus FindStatus = Find(key, hiter);
500  if (FindStatus != XN_STATUS_OK)
501  {
502  // no such entry!
503  return FindStatus;
504  }
505 
506  // Remove by iterator
507  value = hiter.Value();
508  return Remove(hiter);
509  }
510 
521  {
522  if (iter == end())
523  {
524  // Can't remove invalid node
525  return XN_STATUS_ILLEGAL_POSITION;
526  }
527 
528  // Get value and key, to return to the caller
529  value = iter.Value();
530  key = iter.Key();
531 
532  return Remove(iter);
533  }
534 
543  {
544  if (iter == end())
545  {
546  // Can't remove invalid node
547  return XN_STATUS_ILLEGAL_POSITION;
548  }
549 
550  XnNode* pNode = iter.GetNode();
551 
552  XnNode* pKeyNode = (XnNode*)(pNode->Data());
553  XnNode* pValueNode = pKeyNode->Next();
554 
555  // Return the nodes to the pool
556  XnNode::Deallocate(pKeyNode);
557  XnNode::Deallocate(pValueNode);
558 
559  pNode->Previous()->Next() = pNode->Next();
560  pNode->Next()->Previous() = pNode->Previous();
561 
562  XnNode::Deallocate(pNode);
563 
564  return XN_STATUS_OK;
565  }
566 
567 
572  {
573  while (begin() != end())
574  Remove(begin());
575 
576  return XN_STATUS_OK;
577  }
578 
582  XnBool IsEmpty() const
583  {
584  return (begin() == end());
585  }
586 
590  XnUInt32 Size() const
591  {
592  XnUInt32 nSize = 0;
593  for (Iterator iter = begin(); iter != end(); ++iter, ++nSize)
594  ;
595 
596  return nSize;
597  }
598 
607  XnStatus Find(const XnKey& key, ConstIterator& hiter) const
608  {
609  return ConstFind(key, hiter);
610  }
611 
620  XnStatus Find(const XnKey& key, Iterator& hiter)
621  {
622  XnStatus nRetVal = XN_STATUS_OK;
623 
624  ConstIterator& it = hiter;
625  nRetVal = ConstFind(key, it);
626  XN_IS_STATUS_OK(nRetVal);
627 
628  return (XN_STATUS_OK);
629  }
630 
635  {
636  return Iterator(this, m_nMinBin, m_Bins[m_nMinBin]->begin());
637  }
638 
643  {
644  return ConstIterator(this, m_nMinBin, m_Bins[m_nMinBin]->begin());
645  }
646 
651  {
653  }
654 
659  {
661  }
662 
671  {
672  if (begin() != end())
673  {
674  return XN_STATUS_IS_NOT_EMPTY;
675  }
676  m_HashFunction = hashFunction;
677  return XN_STATUS_OK;
678  }
679 
688  {
689  if (begin() != end())
690  {
691  return XN_STATUS_IS_NOT_EMPTY;
692  }
693  m_CompareFunction = compareFunction;
694  return XN_STATUS_OK;
695  }
696 
697 protected:
698 
700  {
703 
704  for (int i = 0; i < XN_HASH_NUM_BINS; i++)
705  {
706  m_Bins[i] = NULL;
707  }
708 
709  m_Bins[XN_HASH_LAST_BIN] = XN_NEW(XnList); // We need this for an end() iterator
711 
713  m_CompareFunction = &XnDefaultCompareFunction;
714  m_HashFunction = &XnDefaultHashFunction;
715  return XN_STATUS_OK;
716  }
717 
727  XnStatus Find(const XnKey& key, XnHashValue hashValue, ConstIterator& hiter) const
728  {
729  if (m_Bins[hashValue] != NULL)
730  {
731  hiter = ConstIterator(this, hashValue, m_Bins[hashValue]->begin());
732  for (XnList::ConstIterator iter = m_Bins[hashValue]->begin();
733  iter != m_Bins[hashValue]->end(); ++iter, ++hiter)
734  {
735  if ((*m_CompareFunction)(key, hiter.Key()) == 0)
736  return XN_STATUS_OK;
737  }
738  }
739 
740  return XN_STATUS_NO_MATCH;
741  }
742 
743 
746 
747  XnUInt16 m_nMinBin;
748 
749  /* Status of initialization - could be an error if memory could not be allocated. */
751 
756 
757 private:
758  XnStatus ConstFind(const XnKey& key, ConstIterator& hiter) const
759  {
760  XnHashValue HashValue = (*m_HashFunction)(key);
761  return Find(key, HashValue, hiter);
762  }
763 };
764 
769 #define XN_DECLARE_DEFAULT_KEY_MANAGER_DECL(decl, KeyType, ClassName, KeyTranslator) \
770  class decl ClassName \
771  { \
772  public: \
773  inline static XnHashValue Hash(KeyType const& key) \
774  { \
775  const XnKey _key = KeyTranslator::GetAsValue(key); \
776  return XnDefaultHashFunction(_key); \
777  } \
778  inline static XnInt32 Compare(KeyType const& key1, KeyType const& key2) \
779  { \
780  const XnKey _key1 = KeyTranslator::GetAsValue(key1); \
781  const XnKey _key2 = KeyTranslator::GetAsValue(key2); \
782  return XnDefaultCompareFunction(_key1, _key2); \
783  } \
784  };
785 
790 #define XN_DECLARE_DEFAULT_KEY_MANAGER(KeyType, ClassName, KeyTranslator) \
791  XN_DECLARE_DEFAULT_KEY_MANAGER_DECL(, KeyType, ClassName, KeyTranslator)
792 
798 #define XN_DECLARE_HASH_DECL(decl, KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator, KeyManager) \
799  class decl ClassName : public XnHash \
800  { \
801  public: \
802  class decl ConstIterator : public XnHash::ConstIterator \
803  { \
804  public: \
805  friend class ClassName; \
806  inline ConstIterator(const ConstIterator& other) : XnHash::ConstIterator(other) {} \
807  inline ConstIterator& operator++() \
808  { \
809  ++(*(XnHash::ConstIterator*)this); \
810  return (*this); \
811  } \
812  inline ConstIterator operator++(int) \
813  { \
814  ConstIterator result = *this; \
815  ++*this; \
816  return result; \
817  } \
818  inline ConstIterator& operator--() \
819  { \
820  --(*(XnHash::ConstIterator*)this); \
821  return (*this); \
822  } \
823  inline ConstIterator operator--(int) \
824  { \
825  ConstIterator result = *this; \
826  --*this; \
827  return result; \
828  } \
829  inline KeyType const& Key() const \
830  { \
831  return KeyTranslator::GetFromValue(XnHash::ConstIterator::Key()); \
832  } \
833  inline ValueType const& Value() const \
834  { \
835  return ValueTranslator::GetFromValue(XnHash::ConstIterator::Value()); \
836  } \
837  protected: \
838  inline ConstIterator(const XnHash::ConstIterator& other) : \
839  XnHash::ConstIterator(other) {} \
840  }; \
841  class decl Iterator : public ConstIterator \
842  { \
843  public: \
844  friend class ClassName; \
845  inline Iterator(const Iterator& other) : ConstIterator(other) {} \
846  inline Iterator& operator++() \
847  { \
848  ++(*(ConstIterator*)this); \
849  return (*this); \
850  } \
851  inline Iterator operator++(int) \
852  { \
853  Iterator result = *this; \
854  ++*this; \
855  return result; \
856  } \
857  inline Iterator& operator--() \
858  { \
859  --(*(ConstIterator*)this); \
860  return (*this); \
861  } \
862  inline Iterator operator--(int) \
863  { \
864  Iterator result = *this; \
865  --*this; \
866  return result; \
867  } \
868  inline KeyType& Key() const \
869  { \
870  return (KeyType&)ConstIterator::Key(); \
871  } \
872  inline ValueType& Value() const \
873  { \
874  return (ValueType&)ConstIterator::Value(); \
875  } \
876  protected: \
877  inline Iterator(const XnHash::Iterator& other) : ConstIterator(other) {} \
878  }; \
879  public: \
880  ClassName() \
881  { \
882  SetHashFunction(Hash); \
883  SetCompareFunction(Compare); \
884  } \
885  ClassName(const ClassName& other) \
886  { \
887  SetHashFunction(Hash); \
888  SetCompareFunction(Compare); \
889  *this = other; \
890  } \
891  virtual ~ClassName() \
892  { \
893  while (!IsEmpty()) \
894  Remove(begin()); \
895  } \
896  ClassName& operator=(const ClassName& other) \
897  { \
898  Clear(); \
899  for (ConstIterator it = other.begin(); it != other.end(); it++) \
900  { \
901  m_nInitStatus = Set(it.Key(), it.Value()); \
902  if (m_nInitStatus != XN_STATUS_OK) \
903  { \
904  return *this; \
905  } \
906  } \
907  return *this; \
908  } \
909  XnStatus Set(KeyType const& key, ValueType const& value) \
910  { \
911  Iterator oldIt = begin(); \
912  if (Find(key, oldIt) == XN_STATUS_OK) \
913  { \
914  oldIt.Value() = value; \
915  } \
916  else \
917  { \
918  XnKey _key = KeyTranslator::CreateValueCopy(key); \
919  XnValue _value = ValueTranslator::CreateValueCopy(value); \
920  XnStatus nRetVal = XnHash::Set(_key, _value); \
921  if (nRetVal != XN_STATUS_OK) \
922  { \
923  KeyTranslator::FreeValue(_key); \
924  ValueTranslator::FreeValue(_value); \
925  return (nRetVal); \
926  } \
927  } \
928  return XN_STATUS_OK; \
929  } \
930  XnStatus Get(KeyType const& key, ValueType& value) const \
931  { \
932  XnKey _key = KeyTranslator::GetAsValue(key); \
933  XnValue _value; \
934  XnStatus nRetVal = XnHash::Get(_key, _value); \
935  if (nRetVal != XN_STATUS_OK) return (nRetVal); \
936  value = ValueTranslator::GetFromValue(_value); \
937  return XN_STATUS_OK; \
938  } \
939  XnStatus Get(KeyType const& key, ValueType*& pValue) const \
940  { \
941  XnKey _key = KeyTranslator::GetAsValue(key); \
942  XnValue _value; \
943  XnStatus nRetVal = XnHash::Get(_key, _value); \
944  if (nRetVal != XN_STATUS_OK) return (nRetVal); \
945  pValue = &ValueTranslator::GetFromValue(_value); \
946  return XN_STATUS_OK; \
947  } \
948  XnStatus Remove(KeyType const& key) \
949  { \
950  ValueType dummy; \
951  return Remove(key, dummy); \
952  } \
953  XnStatus Remove(KeyType const& key, ValueType& value) \
954  { \
955  ConstIterator it = end(); \
956  XnStatus nRetVal = Find(key, it); \
957  if (nRetVal != XN_STATUS_OK) return (nRetVal); \
958  value = it.Value(); \
959  return Remove(it); \
960  } \
961  inline XnStatus Remove(ConstIterator iter) \
962  { \
963  XnKey key = KeyTranslator::GetAsValue(iter.Key()); \
964  XnValue value = ValueTranslator::GetAsValue(iter.Value()); \
965  XnStatus nRetVal = XnHash::Remove(iter); \
966  if (nRetVal != XN_STATUS_OK) return (nRetVal); \
967  KeyTranslator::FreeValue(key); \
968  ValueTranslator::FreeValue(value); \
969  return XN_STATUS_OK; \
970  } \
971  XnStatus Find(KeyType const& key, ConstIterator& hiter) const \
972  { \
973  XnKey _key = KeyTranslator::GetAsValue(key); \
974  XnHash::ConstIterator it = XnHash::end(); \
975  XnStatus nRetVal = XnHash::Find(_key, it); \
976  if (nRetVal != XN_STATUS_OK) return (nRetVal); \
977  hiter = it; \
978  return XN_STATUS_OK; \
979  } \
980  XnStatus Find(KeyType const& key, Iterator& hiter) \
981  { \
982  XnKey _key = KeyTranslator::GetAsValue(key); \
983  XnHash::Iterator it = XnHash::end(); \
984  XnStatus nRetVal = XnHash::Find(_key, it); \
985  if (nRetVal != XN_STATUS_OK) return (nRetVal); \
986  hiter = it; \
987  return XN_STATUS_OK; \
988  } \
989  inline Iterator begin() { return XnHash::begin(); } \
990  inline ConstIterator begin() const { return XnHash::begin(); } \
991  inline Iterator end() { return XnHash::end(); } \
992  inline ConstIterator end() const { return XnHash::end(); } \
993  protected: \
994  virtual XnStatus Remove(XnHash::ConstIterator iter) \
995  { \
996  return Remove(ConstIterator(iter)); \
997  } \
998  inline static XnHashValue Hash(const XnKey& key) \
999  { \
1000  KeyType const& _key = KeyTranslator::GetFromValue(key); \
1001  return KeyManager::Hash(_key); \
1002  } \
1003  inline static XnInt32 Compare(const XnKey& key1, const XnKey& key2) \
1004  { \
1005  KeyType const _key1 = KeyTranslator::GetFromValue(key1); \
1006  KeyType const _key2 = KeyTranslator::GetFromValue(key2); \
1007  return KeyManager::Compare(_key1, _key2); \
1008  } \
1009  };
1010 
1015 #define XN_DECLARE_HASH(KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator, KeyManager) \
1016  XN_DECLARE_HASH_DECL(, KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator, KeyManager)
1017 
1018 #define _XN_DEFAULT_KEY_MANAGER_NAME(ClassName) _##ClassName##Manager
1019 
1025 #define XN_DECLARE_DEFAULT_MANAGER_HASH_DECL(decl, KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator) \
1026  XN_DECLARE_DEFAULT_KEY_MANAGER_DECL(decl, KeyType, _XN_DEFAULT_KEY_MANAGER_NAME(ClassName), KeyTranslator) \
1027  XN_DECLARE_HASH_DECL(decl, KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator, _XN_DEFAULT_KEY_MANAGER_NAME(ClassName))
1028 
1033 #define XN_DECLARE_DEFAULT_MANAGER_HASH(decl, KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator) \
1034  XN_DECLARE_DEFAULT_MANAGER_HASH_DECL(, KeyType, ValueType, ClassName, KeyTranslator, ValueTranslator)
1035 
1036 #define _XN_DEFAULT_KEY_TRANSLATOR(ClassName) _##ClassName##KeyTranslator
1037 #define _XN_DEFAULT_VALUE_TRANSLATOR(ClassName) _##ClassName##ValueTranslator
1038 
1044 #define XN_DECLARE_DEFAULT_HASH_DECL(decl, KeyType, ValueType, ClassName) \
1045  XN_DECLARE_DEFAULT_VALUE_TRANSLATOR_DECL(decl, KeyType, _XN_DEFAULT_KEY_TRANSLATOR(ClassName)) \
1046  XN_DECLARE_DEFAULT_VALUE_TRANSLATOR_DECL(decl, ValueType, _XN_DEFAULT_VALUE_TRANSLATOR(ClassName)) \
1047  XN_DECLARE_DEFAULT_MANAGER_HASH_DECL(decl, KeyType, ValueType, ClassName, _XN_DEFAULT_KEY_TRANSLATOR(ClassName), _XN_DEFAULT_VALUE_TRANSLATOR(ClassName))
1048 
1053 #define XN_DECLARE_DEFAULT_HASH(KeyType, ValueType, ClassName) \
1054  XN_DECLARE_DEFAULT_HASH_DECL(, KeyType, ValueType, ClassName)
1055 
1056 #endif // _XN_HASH_H