Мы можем увеличить возможность многократного использования этого ориентированного на ключ шаблона защиты доступа?

Можем мы увеличивать возможность многократного использования для этого ориентированного на ключ шаблона защиты доступа:

class SomeKey { 
    friend class Foo;
    // more friends... ?
    SomeKey() {} 
    // possibly non-copyable too
};

class Bar {
public:
    void protectedMethod(SomeKey); // only friends of SomeKey have access
};

Для предотвращения продолженных недоразумений этот шаблон отличается от идиомы Адвоката-клиента:

  • Это может быть более кратко, чем Адвокат-клиент (поскольку это не включает проксирование через третий класс),
  • Это может разрешить делегацию прав доступа
  • ... но его также более навязчивый на исходном классе (один фиктивный параметр на метод)

(Обсуждение стороны разработало в этом вопросе, таким образом я открываю этот вопрос.)

26
задан Community 23 May 2017 в 12:25
поделиться

1 ответ

Мне нравится эта идиома, и она может стать намного чище и выразительнее.

В стандартном C ++ 03 я считаю, что следующий способ является наиболее простым в использовании и наиболее универсальным. (Хотя не слишком много улучшений. В основном экономия на повторении.) Поскольку параметры шаблона не могут быть друзьями , мы должны использовать макрос для определения ключа доступа:

// define passkey groups
#define EXPAND(pX) pX

#define PASSKEY_1(pKeyname, pFriend1)                             \
        class EXPAND(pKeyname)                                    \
        {                                                         \
        private:                                                  \
            friend EXPAND(pFriend1);                              \
            EXPAND(pKeyname)() {}                                 \
                                                                  \
            EXPAND(pKeyname)(const EXPAND(pKeyname)&);            \
            EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \
        }

#define PASSKEY_2(pKeyname, pFriend1, pFriend2)                   \
        class EXPAND(pKeyname)                                    \
        {                                                         \
        private:                                                  \
            friend EXPAND(pFriend1);                              \
            friend EXPAND(pFriend2);                              \
            EXPAND(pKeyname)() {}                                 \
                                                                  \
            EXPAND(pKeyname)(const EXPAND(pKeyname)&);            \
            EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \
        }
// and so on to some N

//////////////////////////////////////////////////////////
// test!
//////////////////////////////////////////////////////////
struct bar;
struct baz;
struct qux;
void quux(int, double);

struct foo
{
    PASSKEY_1(restricted1_key, struct bar);
    PASSKEY_2(restricted2_key, struct bar, struct baz);
    PASSKEY_1(restricted3_key, void quux(int, double));

    void restricted1(restricted1_key) {}
    void restricted2(restricted2_key) {}
    void restricted3(restricted3_key) {}
} f;

struct bar
{
    void run(void)
    {
        // passkey works
        f.restricted1(foo::restricted1_key());
        f.restricted2(foo::restricted2_key());
    }
};

struct baz
{
    void run(void)
    {
        // cannot create passkey
        /* f.restricted1(foo::restricted1_key()); */

        // passkey works
        f.restricted2(foo::restricted2_key());
    }
};

struct qux
{
    void run(void)
    {
        // cannot create any required passkeys
        /* f.restricted1(foo::restricted1_key()); */
        /* f.restricted2(foo::restricted2_key()); */
    }
};

void quux(int, double)
{
    // passkey words
    f.restricted3(foo::restricted3_key());
}

void corge(void)
{
    // cannot use quux's passkey
    /* f.restricted3(foo::restricted3_key()); */
}

int main(){}

Этот метод имеет два недостатка: 1 ) вызывающий должен знать конкретный ключ доступа, который ему необходимо создать. Хотя простая схема именования ( function_key ) в основном устраняет ее, она все же может быть одним очистителем абстракции (и проще). 2) Хотя использовать макрос не очень сложно, он может показаться немного уродливым, требующим блока определений ключей доступа. Однако в C ++ 03 невозможно устранить эти недостатки.


В C ++ 0x идиома может достигать своей простейшей и наиболее выразительной формы. Это связано как с вариативными шаблонами, так и с тем, что параметры шаблона могут быть друзьями. (Обратите внимание, что MSVC pre-2010 допускает описатели шаблонов друзей в качестве расширения; поэтому можно смоделировать это решение):

// each class has its own unique key only it can create
// (it will try to get friendship by "showing" its passkey)
template <typename T>
class passkey
{
private:
    friend T; // C++0x, MSVC allows as extension
    passkey() {}

    // noncopyable
    passkey(const passkey&) = delete;
    passkey& operator=(const passkey&) = delete;
};

// functions still require a macro. this
// is because a friend function requires
// the entire declaration, which is not
// just a type, but a name as well. we do 
// this by creating a tag and specializing 
// the passkey for it, friending the function
#define EXPAND(pX) pX

// we use variadic macro parameters to allow
// functions with commas, it all gets pasted
// back together again when we friend it
#define PASSKEY_FUNCTION(pTag, pFunc, ...)               \
        struct EXPAND(pTag);                             \
                                                         \
        template <>                                      \
        class passkey<EXPAND(pTag)>                      \
        {                                                \
        private:                                         \
            friend pFunc __VA_ARGS__;                    \
            passkey() {}                                 \
                                                         \
            passkey(const passkey&) = delete;            \
            passkey& operator=(const passkey&) = delete; \
        }

// meta function determines if a type 
// is contained in a parameter pack
template<typename T, typename... List>
struct is_contained : std::false_type {};

template<typename T, typename... List>
struct is_contained<T, T, List...> : std::true_type {};

template<typename T, typename Head, typename... List>
struct is_contained<T, Head, List...> : is_contained<T, List...> {};

// this class can only be created with allowed passkeys
template <typename... Keys>
class allow
{
public:
    // check if passkey is allowed
    template <typename Key>
    allow(const passkey<Key>&)
    {
        static_assert(is_contained<Key, Keys>::value, 
                        "Passkey is not allowed.");
    }

private:
    // noncopyable
    allow(const allow&) = delete;
    allow& operator=(const allow&) = delete;
};

//////////////////////////////////////////////////////////
// test!
//////////////////////////////////////////////////////////
struct bar;
struct baz;
struct qux;
void quux(int, double);

// make a passkey for quux function
PASSKEY_FUNCTION(quux_tag, void quux(int, double));

struct foo
{    
    void restricted1(allow<bar>) {}
    void restricted2(allow<bar, baz>) {}
    void restricted3(allow<quux_tag>) {}
} f;

struct bar
{
    void run(void)
    {
        // passkey works
        f.restricted1(passkey<bar>());
        f.restricted2(passkey<bar>());
    }
};

struct baz
{
    void run(void)
    {
        // passkey does not work
        /* f.restricted1(passkey<baz>()); */

        // passkey works
        f.restricted2(passkey<baz>());
    }
};

struct qux
{
    void run(void)
    {
        // own passkey does not work,
        // cannot create any required passkeys
        /* f.restricted1(passkey<qux>()); */
        /* f.restricted2(passkey<qux>()); */
        /* f.restricted1(passkey<bar>()); */
        /* f.restricted2(passkey<baz>()); */
    }
};

void quux(int, double)
{
    // passkey words
    f.restricted3(passkey<quux_tag>());
}

void corge(void)
{
    // cannot use quux's passkey
    /* f.restricted3(passkey<quux_tag>()); */
}

int main(){}

Обратите внимание, что в большинстве случаев используйте только шаблонный код ( все нефункциональные случаи!) больше ничего не нужно специально определять. Этот код в общем и просто реализует идиому для любой комбинации классов и функций.

Вызывающему абоненту не нужно пытаться создать или запомнить ключ доступа, специфичный для функции.Вместо этого каждый класс теперь имеет свой собственный уникальный ключ доступа, и функция просто выбирает, какой ключ доступа он разрешит, в параметрах шаблона параметра ключа доступа (дополнительных определений не требуется); это устраняет оба недостатка. Вызывающий просто создает свой собственный ключ доступа и звонит с ним, и ему не нужно беспокоиться ни о чем другом.

25
ответ дан 28 November 2019 в 07:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: