Program Listing for File Handles.hpp

Return to documentation for file (include/lvr2/geometry/Handles.hpp)

/*
 * Handles.hpp
 *
 *  @date 15.06.2017
 *  @author Lukas Kalbertodt <lukas.kalbertodt@gmail.com>
 */

#ifndef LVR2_GEOMETRY_HANDLES_H_
#define LVR2_GEOMETRY_HANDLES_H_

#include <cstdint>
#include <functional>

#include "lvr2/util/BaseHandle.hpp"

namespace lvr2
{

using Index = pmp::IndexType;

// Note on strongly typed handles:
//
// You might ask: Why do we need that many classes for handles? Wouldn't one
// be enough? Or you go even further: if every handle is just a simple integer,
// why not store the integer directly. Well, it all comes down to "strong
// typing".
//
// Type systems are the main way for compilers to notice that a program is
// faulty. While compiler errors are just annoying in the first few years of
// learning how to program, they become very useful later on. When writing
// software, humans will make mistakes -- that's just a fact. The question is
// WHEN we want to notice those mistakes. There are a few possibilities here:
//
// - while compiling
// - while executing unit tests
// - in production
//
// No one wants to notice bugs when already running software in production. Thus
// we want to notice our mistakes earlier. Since this whole library clearly
// doesn't care about unit tests, mistakes can only be noticed either at
// compile time or when the developer executes the program.
//
// Well, now the fun parts. When you use a language with a sufficiently
// powerful type system (as C++) and if you are correctly using this type
// system, you can avoid many huge classes of bugs! The compiler will tell you
// right away, when you made a mistake.
//
// So with these strongly typed handles, you cannot falsely assign an EdgeHandle
// to a FaceHandle -- it will result in a compiler error. If you were using
// simple integers, the compiler wouldn't notice and you would have to track
// down the bug manually. Not so great.
//
// Apart from that: it makes reading code so much easier, as you know exactly
// what a specific parameter is for.

using EdgeHandle = pmp::Edge;

using FaceHandle = pmp::Face;

using VertexHandle = pmp::Vertex;

class ClusterHandle : public BaseHandle
{
    using BaseHandle::BaseHandle;
};

class TextureHandle : public BaseHandle
{
    using BaseHandle::BaseHandle;
};

class OptionalEdgeHandle : public BaseOptionalHandle<EdgeHandle>
{
    using BaseOptionalHandle<EdgeHandle>::BaseOptionalHandle;
};

class OptionalFaceHandle : public BaseOptionalHandle<FaceHandle>
{
    using BaseOptionalHandle<FaceHandle>::BaseOptionalHandle;
};

class OptionalVertexHandle : public BaseOptionalHandle<VertexHandle>
{
    using BaseOptionalHandle<VertexHandle>::BaseOptionalHandle;
};

class OptionalClusterHandle : public BaseOptionalHandle<ClusterHandle>
{
    using BaseOptionalHandle<ClusterHandle>::BaseOptionalHandle;
};

inline std::ostream& operator<<(std::ostream& os, const ClusterHandle& h)
{
    return (os << 'c' << h.idx());
}

inline std::ostream& operator<<(std::ostream& os, const OptionalEdgeHandle& h)
{
    if (h)
    {
        os << "E" << h.unwrap().idx();
    }
    else
    {
        os << "E⊥";
    }
    return os;
}

inline std::ostream& operator<<(std::ostream& os, const OptionalFaceHandle& h)
{
    if (h)
    {
        os << "F" << h.unwrap().idx();
    }
    else
    {
        os << "F⊥";
    }
    return os;
}

inline std::ostream& operator<<(std::ostream& os, const OptionalVertexHandle& h)
{
    if (h)
    {
        os << "V" << h.unwrap().idx();
    }
    else
    {
        os << "V⊥";
    }
    return os;
}

inline std::ostream& operator<<(std::ostream& os, const OptionalClusterHandle& h)
{
    if (h)
    {
        os << "C" << h.unwrap().idx();
    }
    else
    {
        os << "C⊥";
    }
    return os;
}

} // namespace lvr2

namespace std
{
    IMPL_HANDLE_HASH(lvr2::ClusterHandle);
    IMPL_HANDLE_HASH(lvr2::TextureHandle);
} // namespace std

#endif /* LVR2_GEOMETRY_HANDLES_H_ */