blob: 3e9a7c7a6e3fe39a1d5fc907d0406c04012c6ab7 [file] [log] [blame]
$$ This is a pump file for generating file templates. Pump is a python
$$ script that is part of the Google Test suite of utilities. Description
$$ can be found here:
$$
$$ http://code.google.com/p/googletest/wiki/PumpManual
$$
$$ Maximum number of different member types in a union.
$var MAX_MEMBERS = 5
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COBALT_SCRIPT_UNION_TYPE_H_
#define COBALT_SCRIPT_UNION_TYPE_H_
// Implementation of IDL union types.
// http://heycam.github.io/webidl/#idl-union
//
// The member types for a given union type are set at compile-time as template
// parameters. Use templated IsType<> functions to check if a certain type is
// the specific type.
// http://heycam.github.io/webidl/#dfn-specific-type
// The template function AsType can be used to return the specific type.
//
// Attempting to instantiate a UnionType with an unsupported type will result
// in a compile-time assert.
//
// Attempting to query or retrieve a type that is not a part of the union will
// result in a compile-time assert.
//
// Each UnionTypeN allows for definition of a union type with N members. The
// template parameters should be the flattened member types of the union:
// http://heycam.github.io/webidl/#dfn-flattened-union-member-types
//
// Per the specification, there should be either 0 or 1 nullable types in union.
// In the case that there is one nullable type, the entire union type should
// be declared as nullable (with base::Optional<>). A corollary to this is that
// none of the member types in the UnionTypeN template should be nullable.
#include <iosfwd>
#include <limits>
#include "base/memory/aligned_memory.h"
#include "cobalt/script/union_type_internal.h"
namespace cobalt {
namespace script {
$$ Skip the case where there is only one type
$range NUM_MEMBERS 2..MAX_MEMBERS
$for NUM_MEMBERS [[
$range TYPE 1..NUM_MEMBERS
template <$for TYPE , [[typename T$(TYPE)]]>
class UnionType$(NUM_MEMBERS) {
public:
$range CTOR_TYPE 2..NUM_MEMBERS
UnionType$(NUM_MEMBERS)() : specific_type_(kUnspecified) {}
$for TYPE [[
explicit UnionType$(NUM_MEMBERS)([[typename internal::UnionTypeTraits<T$(TYPE)>::ArgType arg]])
: specific_type_(kTypeT$(TYPE)) {
new (storage_.void_data()) T$(TYPE)(arg);
}
]]
UnionType$(NUM_MEMBERS)(const UnionType$(NUM_MEMBERS)& other) {
ConstructFromOther(other);
}
UnionType$(NUM_MEMBERS)& operator=(const UnionType$(NUM_MEMBERS)& other) {
if (&other != this) {
Destruct();
ConstructFromOther(other);
}
return *this;
}
~UnionType$(NUM_MEMBERS)() {
Destruct();
}
// Forward these checks to the UnionTypeCheck helper class, which works around
// being unable to do template specializations in class scope.
template <typename S>
bool IsType() const {
return UnionTypeCheck<S>::IsType(this);
}
template <typename S>
typename internal::UnionTypeTraits<S>::ReturnType AsType() {
return UnionTypeCheck<S>::AsType(this);
}
template <typename S>
typename internal::UnionTypeTraits<S>::ConstReturnType AsType() const {
return UnionTypeCheck<S>::AsType(this);
}
private:
// Internal helper class for checking and getting the union's specific type.
// Only partial class template specializations are allowed in class scope,
// hence the extra dummy template variable.
template <typename U, bool = false>
class UnionTypeCheck {
// Attempting to query for types that are not part of the union will
// result in a compile-time error.
COMPILE_ASSERT(sizeof(U) == 0, UnsupportedType);
};
// Specializations of the UnionTypeCheck class for each member type of the
// union.
$for TYPE [[
template <bool dummy>
class UnionTypeCheck<T$(TYPE), dummy> {
static bool IsType(const UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>* union_value) {
return union_value->specific_type_ == kTypeT$(TYPE);
}
static typename internal::UnionTypeTraits<T$(TYPE)>::ReturnType
AsType(UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>* union_value) {
return *(union_value->storage_.template data_as<T$(TYPE)>());
}
static typename internal::UnionTypeTraits<T$(TYPE)>::ConstReturnType
AsType(const UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>* union_value) {
return *(union_value->storage_.template data_as<T$(TYPE)>());
}
friend class UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>;
};
]]
enum SpecificType {
kUnspecified = 0,
$for TYPE [[
kTypeT$(TYPE),
]]
};
union StorageUnion {
$for TYPE [[
base::AlignedMemory<sizeof(T$(TYPE)), SB_ALIGNOF(T$(TYPE))> t$(TYPE);
]]
};
void ConstructFromOther(const UnionType$(NUM_MEMBERS)& other) {
specific_type_ = other.specific_type_;
switch (specific_type_) {
$for TYPE [[
case kTypeT$(TYPE):
new (storage_.void_data()) T$(TYPE)(other.AsType<T$(TYPE)>());
break;
]]
case kUnspecified:
// no-op
break;
}
}
void Destruct() {
switch (specific_type_) {
$for TYPE [[
case kTypeT$(TYPE):
storage_.template data_as<T$(TYPE)>()->T$(TYPE)::~T$(TYPE)();
break;
]]
case kUnspecified:
// no-op
break;
}
specific_type_ = kUnspecified;
}
base::AlignedMemory<sizeof(StorageUnion), SB_ALIGNOF(StorageUnion)> storage_;
SpecificType specific_type_;
// Count the number of numeric types in this union. There can be a max of one.
// Otherwise, the JS->Cobalt conversion is ambiguous.
// The spec doesn't seem to describe this limitation, but this is what Blink
// does.
static const int kNumNumericTypes =
$for TYPE +
[[(internal::UnionTypeTraits<T$(TYPE)>::is_numeric_type ? 1 : 0)]];
COMPILE_ASSERT(kNumNumericTypes <= 1, AmbiguousUnionTypeConversion);
};
// Needed to instantiate base::Optional<UnionTypeN>
template <$for TYPE , [[typename T$(TYPE)]]>
inline std::ostream& operator<<(
std::ostream& stream, const UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>& union_value) {
$for TYPE [[ if (union_value.template IsType<T$(TYPE)>()) {
stream << union_value.template AsType<T$(TYPE)>();
} else
]] {
stream << "Undefined union type.";
}
return stream;
}
]] $$ for NUM_MEMBERS
} // namespace script
} // namespace cobalt
#endif // COBALT_SCRIPT_UNION_TYPE_H_