简体   繁体   中英

How to set Variadic CRTP base classes to be friend's of the derived class

Main parts of the problem is using CRTP with a policy based design and variadic template . From the policy can't reach the protected or private members from the main/derived class. Because of using variadic template I can't declare policies just as friends.

The question is, how can I set all of the policy classes as friends of derived class.

Given this CRTP solution, what is support multiple inheritance levels and solved the diamond problems without virtual inheritance.

// Derived     - We would like to obtain access to this type of instance
// BaseDerived - Helper type to avoid the diamond problem without virtual inheritance
template<typename Derived, template<typename> class BaseDerived>
class Crtp {
protected:
    [[nodiscard]] constexpr Derived & underlying() noexcept
    {
        return static_cast<Derived &>(*this);
    }

    [[nodiscard]] constexpr Derived const & underlying() const noexcept
    {
        return static_cast<Derived const &>(*this);
    }
};

// Helper struct to achive multiple inheritance
struct NoDerivedClassTag;

template<template<typename> class Derived, typename Substitute, template<typename> class Base>
using DerivedCrtpBase = Base<std::conditional_t<std::is_same_v<Substitute, NoDerivedClassTag>, Derived<NoDerivedClassTag>, Substitute>>;

template<template<typename> class Interface, typename Object>
using is_crtp_interface_of = std::enable_if_t<
    std::is_same_v<Interface<NoDerivedClassTag>, Object> || std::is_base_of_v<Interface<typename Object::exact_type>, Object>>;

Using this CRTP solution in a policy based design with variadic template like this

template<template<typename> class... Functionality>
class FinalDerived
    : public Functionality<FinalDerived<Functionality...>>...
{
public:
    constexpr int get() const
    {
        return protected_variable_;
    }

// Remove to check the problem
//protected:
    int protected_variable_ {-1};
};

The goal is to use the protected variable from the policy like this

template<typename Derived>
struct Increment
    : Crtp<Derived, Increment>
{
    void increment(int an_value)
    {
        this->underlying().protected_variable_ += an_value;
    }
};

template<typename Derived>
struct Decrement
    : Crtp<Derived, Decrement>
{
    void decrement(int an_value)
    {
        this->underlying().protected_variable_ -= an_value;
    }
};

Example of the usage

constexpr int number {7};

int main(void){
    FinalDerived<Increment, Decrement> derived;

    std::cout << "start: " << derived.get() << "\n";

    derived.increment(number);
    std::cout << "incremented: " << derived.get() << "\n";

    derived.decrement(number);
    std::cout << "decremented: " << derived.get() << "\n";
}

Runnable example

A solution I can think of is to use trait classes, inherited by classes you create and defined before your CRTP base and policies, so they would be complete classes. Trait class may contain a pointer or reference to the member you need to reach, the type declarations shared by classes with those traits, etc., see how it's done in standard components

As Evg wrote it, has an old question with a correct answer.

#ifndef CRTP_VARIADIC_FRIEND

// M - Macro
// N - Number
// P - Packed parameters
// T - Type
#    define CRTP_FRIEND_REPEAT_2(M, N, P, T) M(N, P, T) M(N + 1, P, T)
#    define CRTP_FRIEND_REPEAT_4(M, N, P, T) CRTP_FRIEND_REPEAT_2(M, N, P, T) CRTP_FRIEND_REPEAT_2(M, N + 2, P, T)
#    define CRTP_FRIEND_REPEAT_8(M, N, P, T) CRTP_FRIEND_REPEAT_4(M, N, P, T) CRTP_FRIEND_REPEAT_4(M, N + 4, P, T)
#    define CRTP_FRIEND_REPEAT_16(M, N, P, T) CRTP_FRIEND_REPEAT_8(M, N, P, T) CRTP_FRIEND_REPEAT_8(M, N + 8, P, T)
#    define CRTP_FRIEND_REPEAT_32(M, N, P, T) CRTP_FRIEND_REPEAT_16(M, N, P, T) CRTP_FRIEND_REPEAT_16(M, N + 16, P, T)
#    define CRTP_FRIEND_REPEAT_64(M, N, P, T) CRTP_FRIEND_REPEAT_32(M, N, P, T) CRTP_FRIEND_REPEAT_32(M, N + 32, P, T)
#    define CRTP_FRIEND_REPEAT_128(M, N, P, T) CRTP_FRIEND_REPEAT_64(M, N, P, T) CRTP_FRIEND_REPEAT_64(M, N + 64, P, T)

#    define CRTP_FRIEND(N, P, T) \
        friend std::tuple_element_t<std::min(static_cast<std::size_t>(N + 1), sizeof...(P)), std::tuple<void, T...>>;

#    define CRTP_VARIADIC_FRIEND(P, T) CRTP_FRIEND_REPEAT_128(CRTP_FRIEND, 0, P, T)

#endif

I will try the Trait class solution. It will be an other answer later.

Runnable example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM