Skip to content
Snippets Groups Projects
Commit 69e5fd19 authored by Christoph Grüninger's avatar Christoph Grüninger
Browse files

[!87] Add GeometryType::Id to allow using GeometryType as a template parameter

Merge branch 'feature/add-geometrytype-id' into 'master'

ref:core/dune-geometry After thinking about what I proposed in [#17], I
realized that an index is probably not the right idea of thinking about this.
Instead, we can just do a 1-to-1 mapping of GeometryType to a canonical id, as
GeometryType itself is just a 8 byte struct that stores three integral values.

I've nested the id within GeometryType as GeometryType::Id here and added
implicit conversions between the two. The id type is a scoped enum to avoid
any future nastiness with implicit conversions to other integral types.

With this merge request, you can have a template with a GeometryType parameter
with just a tiny bit of overhead on the implementor's side:

    // note the additional "::Id"
    template<GeometryType::Id gt_>
    class Foo
    {
      // reconstruct the GeometryType
      static constexpr GeometryType gt = gt_;
    };

For the user, it looks like they can just plug in a GeometryType:

    Foo<GeometryTypes::triangle> foo;

This solves a major 2.6 headache in PDELab (see [#17],
[pdelab/dune-pdelab#102]), so I'd really like this to go into 2.6.

Closes [#17].

See merge request [!87]

  [#17]: gitlab.dune-project.org/NoneNone/issues/17
  [pdelab/dune-pdelab#102]: gitlab.dune-project.org/pdelab/dune-pdelab/issues/102
  [!87]: gitlab.dune-project.org/core/dune-geometry/merge_requests/87


Closes #17
parents 38586f59 04302751
No related branches found
No related tags found
1 merge request!87Add GeometryType::Id to allow using GeometryType as a template parameter
Pipeline #23079 passed
......@@ -76,3 +76,6 @@ dune_add_test(SOURCES test-refinement.cc
dune_add_test(SOURCES test-constexpr-geometrytype.cc
LINK_LIBRARIES dunegeometry)
dune_add_test(SOURCES test-geometrytype-id.cc
LINK_LIBRARIES dunegeometry)
#include <config.h>
#include <dune/geometry/type.hh>
template<Dune::GeometryType::Id gtid>
struct Foo
{
static constexpr Dune::GeometryType gt = gtid;
};
int main(int argc, char** argv)
{
// make sure we can correctly roundtrip between GeometryType
// and its Id in constexpr context
constexpr Dune::GeometryType gt1 = Dune::GeometryTypes::triangle;
Foo<gt1> foo;
constexpr Dune::GeometryType gt2 = foo.gt;
static_assert(gt1 == gt2, "The two geometry types have to compare equal");
return 0;
}
......@@ -271,6 +271,8 @@ namespace Dune
* GeometryType is a C++ "literal type" and can be used in `constexpr` context if created
* with a `constexpr` constructor.
*
* If you want to use a GeometryType as a template parameter, see GeometryType::Id.
*
* \ingroup GeometryType
*/
class GeometryType
......@@ -300,8 +302,77 @@ namespace Dune
/** \brief Topology Id element */
unsigned int topologyId_;
// Internal type used for the Id. The exact nature of this type is kept
// as an implementation detail on purpose. We use a scoped enum here because scoped enums
// can be used as template parameters, but are not implicitly converted to other integral
// types by the compiler. That way, we avoid unfortunate implicit conversion chains, e.g.
// people trying to work with GlobalGeometryTypeIndex, but forgetting to actually call
// GlobalGeometryTypeIndex::index(gt) and just using gt directly.
enum class IdType : std::uint64_t
{};
public:
/** \brief An integral id representing a GeometryType. */
/**
* Id is an unspecified built-in integral type that uniquely represents a GeometryType.
* It mostly exists to be able to use a geometry type as a template parameter, as C++
* does not let us use GeometryType directly for this purpose.
*
* GeometryType and GeometryType::Id are implicitly convertible to each other, while the
* Id does not implicitly convert into other integral types. They should be used as follows:
*
\code
// define a template with a GeometryType::Id parameter
template<GeometryType::Id gtid>
class Foo
{
// reconstruct a full-blown constexpr GeometryType as needed to access
// information like the dimension etc.
static constexpr GeometryType gt = gtid;
};
// Instantiate a Foo template
Foo<GeometryTypes::triangle> foo;
\endcode
*
* As you can see, the conversion between GeometryType and the id is completely transparent
* to the user (apart from the slightly different template parameter type).
*
* \note The Id really only exists for this template parameter workaround. Do not use it to
* store a more compact version of the GeometryType - GeometryType and GeometryType::Id
* use the same amount of storage (64 bits).
*/
using Id = IdType;
/** \brief Construct an Id representing this GeometryType. */
/**
* This constructor exists mostly to transparently support using a GeometryType as a
* template parameter.
*
* \sa Id
*/
constexpr operator Id() const
{
// recreate the exact storage layout that this class is using, making conversion
// extremely cheap
std::uint64_t id = dim_ | (std::uint64_t(none_) << 8) | (std::uint64_t(topologyId_) << 32);
return static_cast<Id>(id);
}
/** \brief Reconstruct a Geometry type from a GeometryType::Id */
/**
* This constructor exists mostly to transparently support using a GeometryType as a
* template parameter.
*
* \sa Id
*/
constexpr GeometryType(Id id)
: dim_(static_cast<std::uint64_t>(id) & 0xFF)
, none_(static_cast<std::uint64_t>(id) & 0x100)
, topologyId_(static_cast<std::uint64_t>(id) >> 32)
{}
/** @name Constructors */
/*@{*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment