|
| 1 | +//===- COMExtras.h - Helper files for COM operations -------------*- C++-*-===// |
| 2 | +// |
| 3 | +// The LLVM Compiler Infrastructure |
| 4 | +// |
| 5 | +// This file is distributed under the University of Illinois Open Source |
| 6 | +// License. See LICENSE.TXT for details. |
| 7 | +// |
| 8 | +//===----------------------------------------------------------------------===// |
| 9 | + |
| 10 | +#ifndef LLVM_TOOLS_LLVM_PDBDUMP_COMEXTRAS_H |
| 11 | +#define LLVM_TOOLS_LLVM_PDBDUMP_COMEXTRAS_H |
| 12 | + |
| 13 | +#include "llvm/ADT/ArrayRef.h" |
| 14 | +#include "llvm/ADT/SmallVector.h" |
| 15 | + |
| 16 | +#include <tuple> |
| 17 | + |
| 18 | +namespace llvm { |
| 19 | + |
| 20 | +template <typename F> struct function_traits; |
| 21 | + |
| 22 | +#if LLVM_HAS_VARIADIC_TEMPLATES |
| 23 | +template <typename R, typename... Args> |
| 24 | +struct function_traits<R (*)(Args...)> : public function_traits<R(Args...)> {}; |
| 25 | + |
| 26 | +template <typename C, typename R, typename... Args> |
| 27 | +struct function_traits<R (__stdcall C::*)(Args...)> { |
| 28 | + using args_tuple = std::tuple<Args...>; |
| 29 | +}; |
| 30 | +#else |
| 31 | + |
| 32 | +// For the sake of COM, we only need a 3 argument version and a 5 argument |
| 33 | +// version. We could provide 1, 2, 4, and other length of argument lists if |
| 34 | +// this were intended to be more generic. Alternatively, this will "just work" |
| 35 | +// if VS2012 support is dropped and we can use the variadic template case |
| 36 | +// exclusively. |
| 37 | +template <typename C, typename R, typename A1, typename A2, typename A3> |
| 38 | +struct function_traits<R (__stdcall C::*)(A1, A2, A3)> { |
| 39 | + using args_tuple = std::tuple<A1, A2, A3>; |
| 40 | +}; |
| 41 | + |
| 42 | +template <typename C, typename R, typename A1, typename A2, typename A3, |
| 43 | + typename A4, typename A5> |
| 44 | +struct function_traits<R (__stdcall C::*)(A1, A2, A3, A4, A5)> { |
| 45 | + using args_tuple = std::tuple<A1, A2, A3, A4, A5>; |
| 46 | +}; |
| 47 | +#endif |
| 48 | + |
| 49 | +template <class FuncTraits, std::size_t arg> struct function_arg { |
| 50 | + // Writing function_arg as a separate class that accesses the tuple from |
| 51 | + // function_traits is necessary due to what appears to be a bug in MSVC. |
| 52 | + // If you write a nested class inside function_traits like this: |
| 53 | + // template<std::size_t ArgIndex> |
| 54 | + // struct Argument |
| 55 | + // { |
| 56 | + // typedef typename |
| 57 | + // std::tuple_element<ArgIndex, std::tuple<Args...>>::type type; |
| 58 | + // }; |
| 59 | + // MSVC encounters a parsing error. |
| 60 | + typedef |
| 61 | + typename std::tuple_element<arg, typename FuncTraits::args_tuple>::type |
| 62 | + type; |
| 63 | +}; |
| 64 | + |
| 65 | +template <class T> struct remove_double_pointer {}; |
| 66 | +template <class T> struct remove_double_pointer<T **> { typedef T type; }; |
| 67 | + |
| 68 | +namespace sys { |
| 69 | +namespace windows { |
| 70 | + |
| 71 | +/// A helper class for allowing the use of COM enumerators in range-based |
| 72 | +/// for loops. |
| 73 | +/// |
| 74 | +/// A common idiom in the COM world is to have an enumerator interface, say |
| 75 | +/// IMyEnumerator. It's responsible for enumerating over some child data type, |
| 76 | +/// say IChildType. You do the enumeration by calling IMyEnumerator::Next() |
| 77 | +/// one of whose arguments will be an IChildType**. Eventually Next() fails, |
| 78 | +/// indicating that there are no more items. |
| 79 | +/// |
| 80 | +/// com_iterator represents a single point-in-time of this iteration. It is |
| 81 | +/// used by ComEnumerator to support iterating in this fashion via range-based |
| 82 | +/// for loops and other common C++ paradigms. |
| 83 | +template <class EnumeratorType, std::size_t ArgIndex> class com_iterator { |
| 84 | + using FunctionTraits = function_traits<decltype(&EnumeratorType::Next)>; |
| 85 | + typedef typename function_arg<FunctionTraits, ArgIndex>::type FuncArgType; |
| 86 | + // FuncArgType is now something like ISomeCOMInterface **. Remove both |
| 87 | + // pointers, so we can make a CComPtr<T> out of it. |
| 88 | + typedef typename remove_double_pointer<FuncArgType>::type EnumDataType; |
| 89 | + |
| 90 | + CComPtr<EnumeratorType> EnumeratorObject; |
| 91 | + CComPtr<EnumDataType> CurrentItem; |
| 92 | + |
| 93 | +public: |
| 94 | + typedef CComPtr<EnumDataType> value_type; |
| 95 | + typedef std::ptrdiff_t difference_type; |
| 96 | + typedef value_type *pointer_type; |
| 97 | + typedef value_type &reference_type; |
| 98 | + typedef std::forward_iterator_tag iterator_category; |
| 99 | + |
| 100 | + explicit com_iterator(CComPtr<EnumeratorType> Enumerator, |
| 101 | + CComPtr<EnumDataType> Current) |
| 102 | + : EnumeratorObject(Enumerator), CurrentItem(Current) {} |
| 103 | + com_iterator() {} |
| 104 | + |
| 105 | + com_iterator &operator++() { |
| 106 | + // EnumeratorObject->Next() expects CurrentItem to be NULL. |
| 107 | + CurrentItem.Release(); |
| 108 | + ULONG Count = 0; |
| 109 | + HRESULT hr = EnumeratorObject->Next(1, &CurrentItem, &Count); |
| 110 | + if (FAILED(hr) || Count == 0) |
| 111 | + *this = com_iterator(); |
| 112 | + |
| 113 | + return *this; |
| 114 | + } |
| 115 | + |
| 116 | + value_type operator*() { return CurrentItem; } |
| 117 | + |
| 118 | + bool operator==(const com_iterator &other) const { |
| 119 | + return (EnumeratorObject == other.EnumeratorObject) && |
| 120 | + (CurrentItem == other.CurrentItem); |
| 121 | + } |
| 122 | + |
| 123 | + bool operator!=(const com_iterator &other) const { return !(*this == other); } |
| 124 | + |
| 125 | + com_iterator &operator=(const com_iterator &other) { |
| 126 | + EnumeratorObject = other.EnumeratorObject; |
| 127 | + CurrentItem = other.CurrentItem; |
| 128 | + return *this; |
| 129 | + } |
| 130 | +}; |
| 131 | + |
| 132 | +/// ComEnumerator implements the interfaced required for C++ to allow its use |
| 133 | +/// in range-based for loops. In particular, a begin() and end() method. |
| 134 | +/// These methods simply construct and return an appropriate ComIterator |
| 135 | +/// instance. |
| 136 | +template <class EnumeratorType, std::size_t ArgIndex> class com_enumerator { |
| 137 | + typedef function_traits<decltype(&EnumeratorType::Next)> FunctionTraits; |
| 138 | + typedef typename function_arg<FunctionTraits, ArgIndex>::type FuncArgType; |
| 139 | + typedef typename remove_double_pointer<FuncArgType>::type EnumDataType; |
| 140 | + |
| 141 | + CComPtr<EnumeratorType> EnumeratorObject; |
| 142 | + |
| 143 | +public: |
| 144 | + com_enumerator(CComPtr<EnumeratorType> Enumerator) |
| 145 | + : EnumeratorObject(Enumerator) {} |
| 146 | + |
| 147 | + com_iterator<EnumeratorType, ArgIndex> begin() { |
| 148 | + if (!EnumeratorObject) |
| 149 | + return end(); |
| 150 | + |
| 151 | + EnumeratorObject->Reset(); |
| 152 | + ULONG Count = 0; |
| 153 | + CComPtr<EnumDataType> FirstItem; |
| 154 | + HRESULT hr = EnumeratorObject->Next(1, &FirstItem, &Count); |
| 155 | + return (FAILED(hr) || Count == 0) ? end() |
| 156 | + : com_iterator<EnumeratorType, ArgIndex>( |
| 157 | + EnumeratorObject, FirstItem); |
| 158 | + } |
| 159 | + |
| 160 | + com_iterator<EnumeratorType, ArgIndex> end() { |
| 161 | + return com_iterator<EnumeratorType, ArgIndex>(); |
| 162 | + } |
| 163 | +}; |
| 164 | + |
| 165 | +/// A helper class for allowing the use of COM record enumerators in range- |
| 166 | +/// based for loops. |
| 167 | +/// |
| 168 | +/// A record enumerator is almost the same as a regular enumerator, except |
| 169 | +/// that it returns raw byte-data instead of interfaces to other COM objects. |
| 170 | +/// As a result, the enumerator's Next() method has a slightly different |
| 171 | +/// signature, and an iterator dereferences to an ArrayRef instead of a |
| 172 | +/// CComPtr. |
| 173 | +template <class EnumeratorType> class com_data_record_iterator { |
| 174 | +public: |
| 175 | + typedef llvm::ArrayRef<uint8_t> value_type; |
| 176 | + typedef std::ptrdiff_t difference_type; |
| 177 | + typedef value_type *pointer_type; |
| 178 | + typedef value_type &reference_type; |
| 179 | + typedef std::forward_iterator_tag iterator_category; |
| 180 | + |
| 181 | + explicit com_data_record_iterator(CComPtr<EnumeratorType> enumerator) |
| 182 | + : Enumerator(enumerator), CurrentRecord(0) { |
| 183 | + // Make sure we start at the beginning. If there are no records, |
| 184 | + // immediately set ourselves equal to end(). |
| 185 | + if (enumerator) |
| 186 | + enumerator->Reset(); |
| 187 | + |
| 188 | + if (!ReadNextRecord()) |
| 189 | + *this = com_data_record_iterator(); |
| 190 | + } |
| 191 | + com_data_record_iterator() {} |
| 192 | + |
| 193 | + com_data_record_iterator &operator++() { |
| 194 | + ++CurrentRecord; |
| 195 | + // If we can't read any more records, either because there are no more |
| 196 | + // or because we encountered an error, we should compare equal to end. |
| 197 | + if (!ReadNextRecord()) |
| 198 | + *this = com_data_record_iterator(); |
| 199 | + return *this; |
| 200 | + } |
| 201 | + |
| 202 | + value_type operator*() { |
| 203 | + return llvm::ArrayRef<uint8_t>(RecordData.begin(), RecordData.end()); |
| 204 | + } |
| 205 | + |
| 206 | + bool operator==(const com_data_record_iterator &other) const { |
| 207 | + return (Enumerator == other.Enumerator) && |
| 208 | + (CurrentRecord == other.CurrentRecord); |
| 209 | + } |
| 210 | + |
| 211 | + bool operator!=(const com_data_record_iterator &other) const { |
| 212 | + return !(*this == other); |
| 213 | + } |
| 214 | + |
| 215 | +private: |
| 216 | + bool ReadNextRecord() { |
| 217 | + RecordData.clear(); |
| 218 | + ULONG Count = 0; |
| 219 | + DWORD RequiredBufferSize; |
| 220 | + HRESULT hr = Enumerator->Next(1, 0, &RequiredBufferSize, nullptr, &Count); |
| 221 | + if (hr == S_OK) { |
| 222 | + RecordData.resize(RequiredBufferSize); |
| 223 | + DWORD BytesRead = 0; |
| 224 | + hr = Enumerator->Next(1, RequiredBufferSize, &BytesRead, |
| 225 | + RecordData.data(), &Count); |
| 226 | + } |
| 227 | + if (hr != S_OK) |
| 228 | + RecordData.clear(); |
| 229 | + return (hr == S_OK); |
| 230 | + } |
| 231 | + |
| 232 | + CComPtr<EnumeratorType> Enumerator; |
| 233 | + uint32_t CurrentRecord; |
| 234 | + llvm::SmallVector<uint8_t, 32> RecordData; |
| 235 | +}; |
| 236 | + |
| 237 | +/// Similar to ComEnumerator, com_data_record_enumerator implements the range |
| 238 | +/// interface for ComDataRecordIterators. |
| 239 | +template <class EnumeratorType> class com_data_record_enumerator { |
| 240 | +public: |
| 241 | + com_data_record_enumerator(CComPtr<EnumeratorType> enumerator) |
| 242 | + : Enumerator(enumerator) {} |
| 243 | + |
| 244 | + com_data_record_iterator<EnumeratorType> begin() { |
| 245 | + return com_data_record_iterator<EnumeratorType>(Enumerator); |
| 246 | + } |
| 247 | + |
| 248 | + com_data_record_iterator<EnumeratorType> end() { |
| 249 | + LONG NumElts = 0; |
| 250 | + HRESULT hr = Enumerator->get_Count(&NumElts); |
| 251 | + return (FAILED(hr)) ? com_data_record_iterator<EnumeratorType>(Enumerator) |
| 252 | + : com_data_record_iterator<EnumeratorType>(); |
| 253 | + } |
| 254 | + |
| 255 | +private: |
| 256 | + CComPtr<EnumeratorType> Enumerator; |
| 257 | +}; |
| 258 | + |
| 259 | +/// com_enumerator is a simple helper function to allow the enumerator |
| 260 | +/// class's type to be inferred automatically. |
| 261 | +/// This allows you to write simply: |
| 262 | +/// for (auto item : com_enumerator(MyEnumerator)) { |
| 263 | +/// } |
| 264 | +template <class EnumeratorType> |
| 265 | +com_enumerator<EnumeratorType, 1> |
| 266 | +make_com_enumerator(CComPtr<EnumeratorType> Enumerator) { |
| 267 | + return com_enumerator<EnumeratorType, 1>(Enumerator); |
| 268 | +} |
| 269 | + |
| 270 | +/// com_data_record_enumerator is a simple helper function to allow the |
| 271 | +/// enumerator class's type to be inferred automatically. |
| 272 | +/// This allows you to write simply: |
| 273 | +/// for (auto item : com_data_record_enumerator(MyEnumerator)) { |
| 274 | +/// } |
| 275 | +//============================================================================= |
| 276 | +template <class EnumeratorType> |
| 277 | +com_data_record_enumerator<EnumeratorType> |
| 278 | +make_com_data_record_enumerator(CComPtr<EnumeratorType> Enumerator) { |
| 279 | + return com_data_record_enumerator<EnumeratorType>(Enumerator); |
| 280 | +} |
| 281 | + |
| 282 | +} // namespace windows |
| 283 | +} // namespace sys |
| 284 | +} // namespace llvm |
| 285 | + |
| 286 | +#endif |
0 commit comments