2222#include < google/bigtable/v2/data.pb.h>
2323#include < google/bigtable/v2/types.pb.h>
2424#include < cmath>
25+ #include < vector>
2526
2627namespace google {
2728namespace cloud {
@@ -73,12 +74,32 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
7374 * BYTES | `google::cloud::bigtable::Bytes`
7475 * TIMESTAMP | `google::cloud::bigtable::Timestamp`
7576 * DATE | `absl::CivilDay`
77+ * ARRAY | `std::vector<T>` // [1]
78+ *
79+ * [1] The type `T` may be any of the other supported types, except for
80+ * ARRAY/`std::vector`.
7681 *
7782 * Callers may create instances by passing any of the supported values
7883 * (shown in the table above) to the constructor. "Null" values are created
7984 * using the `MakeNullValue<T>()` factory function or by passing an empty
8085 * `absl::optional<T>` to the Value constructor.
8186 *
87+ * @par Bigtable Arrays
88+ *
89+ * Bigtable arrays are represented in C++ as a `std::vector<T>`, where the type
90+ * `T` may be any of the other allowed Bigtable types, such as `bool`,
91+ * `std::int64_t`, etc. The only exception is that arrays may not directly
92+ * contain another array; to achieve a similar result you could create an array
93+ * of a 1-element struct holding an array. The following examples show usage of
94+ * arrays.
95+ *
96+ * @code
97+ * std::vector<std::int64_t> vec = {1, 2, 3, 4, 5};
98+ * bigtable::Value v(vec);
99+ * auto copy = *v.get<std::vector<std::int64_t>>();
100+ * assert(vec == copy);
101+ * @endcode
102+ *
82103 */
83104class Value {
84105 public:
@@ -137,6 +158,24 @@ class Value {
137158 explicit Value (absl::optional<T> opt)
138159 : Value(PrivateConstructor{}, std::move(opt)) {}
139160
161+ /* *
162+ * Constructs an instance from a Bigtable ARRAY of the specified type and
163+ * values.
164+ *
165+ * The type `T` may be any valid type shown above, except vectors of vectors
166+ * are not allowed.
167+ *
168+ * @warning If `T` is a `std::tuple` with field names (i.e., at least one of
169+ * its element types is a `std::pair<std::string, T>`) then, all of the
170+ * vector's elements must have exactly the same field names. Any mismatch
171+ * in in field names results in undefined behavior.
172+ */
173+ template <typename T>
174+ explicit Value (std::vector<T> v) : Value(PrivateConstructor{}, std::move(v)) {
175+ static_assert (!IsVector<std::decay_t <T>>::value,
176+ " vector of vector not allowed. See value.h documentation." );
177+ }
178+
140179 // Copy and move.
141180 Value (Value const &) = default ;
142181 Value (Value&&) = default ;
@@ -197,6 +236,12 @@ class Value {
197236 template <typename T>
198237 struct IsOptional <absl::optional<T>> : std::true_type {};
199238
239+ // Metafunction that returns true if `T` is a std::vector<U>
240+ template <typename T>
241+ struct IsVector : std::false_type {};
242+ template <typename ... Ts>
243+ struct IsVector <std::vector<Ts...>> : std::true_type {};
244+
200245 // Tag-dispatch overloads to check if a C++ type matches the type specified
201246 // by the given `Type` proto.
202247 static bool TypeProtoIs (bool , google::bigtable::v2::Type const &);
@@ -213,6 +258,12 @@ class Value {
213258 google::bigtable::v2::Type const & type) {
214259 return TypeProtoIs (T{}, type);
215260 }
261+ template <typename T>
262+ static bool TypeProtoIs (std::vector<T> const &,
263+ google::bigtable::v2::Type const & type) {
264+ return type.has_array_type () &&
265+ TypeProtoIs (T{}, type.array_type ().element_type ());
266+ }
216267
217268 // Tag-dispatch overloads to convert a C++ type to a `Type` protobuf. The
218269 // argument type is the tag, the argument value is ignored.
@@ -230,6 +281,22 @@ class Value {
230281 static google::bigtable::v2::Type MakeTypeProto (absl::optional<T> const &) {
231282 return MakeTypeProto (T{});
232283 }
284+ template <typename T>
285+ static google::bigtable::v2::Type MakeTypeProto (std::vector<T> const & v) {
286+ google::bigtable::v2::Type t;
287+ t.set_allocated_array_type (
288+ std::move (new google::bigtable::v2::Type_Array ()));
289+ *t.mutable_array_type ()->mutable_element_type () =
290+ MakeTypeProto (v.empty () ? T{} : v[0 ]);
291+ // Checks that vector elements have exactly the same proto Type, which
292+ // includes field names. This is documented UB.
293+ for (auto && e : v) {
294+ google::bigtable::v2::Type vt = MakeTypeProto (e);
295+ if (t.array_type ().element_type ().kind_case () != vt.kind_case ())
296+ internal::ThrowInvalidArgument (" Mismatched types" );
297+ }
298+ return t;
299+ }
233300
234301 // Encodes the argument as a protobuf according to the rules described in
235302 // https://github.com/googleapis/googleapis/blob/master/google/bigtable/v2/type.proto
@@ -251,6 +318,15 @@ class Value {
251318 v.clear_type ();
252319 return v;
253320 }
321+ template <typename T>
322+ static google::bigtable::v2::Value MakeValueProto (std::vector<T> vec) {
323+ google::bigtable::v2::Value v;
324+ auto & list = *v.mutable_array_value ();
325+ for (auto && e : vec) {
326+ *list.add_values () = MakeValueProto (std::move (e));
327+ }
328+ return v;
329+ }
254330
255331 // Tag-dispatch overloads to extract a C++ value from a `Value` protobuf. The
256332 // first argument type is the tag, its value is ignored.
@@ -289,6 +365,36 @@ class Value {
289365 if (!value) return std::move (value).status ();
290366 return absl::optional<T>{*std::move (value)};
291367 }
368+ template <typename T, typename V>
369+ static StatusOr<std::vector<T>> GetValue (
370+ std::vector<T> const &, V&& pv, google::bigtable::v2::Type const & pt) {
371+ if (pv.kind_case () != google::bigtable::v2::Value::kArrayValue ) {
372+ return internal::UnknownError (" missing ARRAY" , GCP_ERROR_INFO ());
373+ }
374+ std::vector<T> v;
375+ for (int i = 0 ; i < pv.array_value ().values ().size (); ++i) {
376+ auto && e = GetProtoListValueElement (std::forward<V>(pv), i);
377+ using ET = decltype (e);
378+ auto value =
379+ GetValue (T{}, std::forward<ET>(e), pt.array_type ().element_type ());
380+ if (!value) return std::move (value).status ();
381+ v.push_back (*std::move (value));
382+ }
383+ return v;
384+ }
385+
386+ // Protocol buffers are not friendly to generic programming, because they use
387+ // different syntax and different names for mutable and non-mutable
388+ // functions. To make GetValue(vector<T>, ...) (above) work, we need split
389+ // the different protobuf syntaxes into overloaded functions.
390+ static google::bigtable::v2::Value const & GetProtoListValueElement (
391+ google::bigtable::v2::Value const & pv, int pos) {
392+ return pv.array_value ().values (pos);
393+ }
394+ static google::bigtable::v2::Value&& GetProtoListValueElement(
395+ google::bigtable::v2::Value&& pv, int pos) {
396+ return std::move (*pv.mutable_array_value ()->mutable_values (pos));
397+ }
292398
293399 // A private templated constructor that is called by all the public
294400 // constructors to set the type_ and value_ members. The `PrivateConstructor`
0 commit comments