我们可以增加这种面向密钥的访问保护模式的可重用性吗? [英] Can we increase the re-usability of this key-oriented access-protection pattern?

查看:179
本文介绍了我们可以增加这种面向密钥的访问保护模式的可重用性吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们可以提高这个重新使用面向关键的访问保护模式

  class SomeKey {
friend class Foo;
//更多的朋友...?
SomeKey(){}
//可能不可复制太
};

class Bar {
public:
void protectedMethod(SomeKey); //只有SomeKey的朋友可以访问
};

为了避免持续的误解,这种模式与律师客户成语:




  • 它可以更简洁律师 - 客户(因为它不涉及通过第三类进行代理)

  • 它可以允许访问权限的授权

  • ...但它还有更多的侵入原有的类(每个方法一个虚拟参数)



这个问题,因此我打开这个问题。 )

解决方案

我喜欢这个成语,它有潜力变得更加干净,更具表现力。 p>

在标准C ++ 03中,我认为以下方式是最容易使用和最通用的。 (尽管不是太多的改进,但主要是重复自己)。因为模板参数不能成为朋友,我们必须使用一个宏来定义passkey:

  //定义密钥组
#define EXPAND(pX)pX

#define PASSKEY_1(pKeyname,pFriend1)\
class EXPAND(pKeyname)\
{\
private: \
朋友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 :\
朋友EXPAND(pFriend1); \
朋友EXPAND(pFriend2); \
EXPAND(pKeyname)(){} \
\
EXPAND(pKeyname)(const EXPAND(pKeyname)&); \
EXPAND(pKeyname)& operator =(const EXPAND(pKeyname)&); \
}
//等等到一些N

//////////////////// ///////////////////////////////
//测试!
////////////////////////////////////////// ////////////
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)
{
//无法创建密钥
/ * f.restricted1(foo: :restricted1_key()); * /

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

struct qux
{
void run(void)
{
//无法创建任何所需的密钥
/ * f.restricted1 FOO :: restricted1_key()); * /
/ * f.restricted2(foo :: restricted2_key()); * /
}
};

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

void corge(void)
{
//不能使用quux的密钥
/ * f.restricted3(foo :: restricted3_key()) ; * /
}

int main(){}

这种方法有两个缺点:1)调用者必须知道需要创建的具体密钥。虽然一个简单的命名方案( function_key )基本上消除了它,但它仍然可以是一个抽象清理器(并且更容易)。 2)虽然使用宏非常困难,但可以看出它有点丑,需要一个密钥定义。然而,C ++ 03中不能对这些缺点进行改进。






在C ++ 0x中,成语可以达到其最简单和最具表现力的形式。这是由于两个可变模板,并允许模板参数成为朋友。 (请注意,2010年之前的MSVC允许模板朋友说明符作为扩展名;因此可以模拟此解决方案):

  //每个class有自己唯一的键只能创建
//(它将尝试通过显示其密钥来获得友谊)
template< typename T>
class passkey
{
private:
friend T; // C ++ 0x,MSVC允许作为扩展名
passkey(){}

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

//函数仍然需要一个宏。这个
//是因为一个朋友的函数需要
//整个声明,这不是
//只是一个类型,而是一个名字。我们通过创建一个标签来创建一个
//这是通过创建一个标签,并专门为
//提供了一个密钥,修改了这个函数
#define EXPAND(pX)pX

//我们使用可变宏参数来允许
//函数与逗号,当我们朋友
#define PASSKEY_FUNCTION(pTag,pFunc,...)时,它们都会再次粘贴
//返回到一起\
struct EXPAND(pTag); \
\
template<> \
class passkey< EXPAND(pTag)> \
{\
private:\
friend pFunc __VA_ARGS__; \
passkey(){} \
\
passkey(const passkey&)= delete; \
密钥& operator =(const passkey&)= delete; \
}

//元函数确定一个类型
//是否包含在一个参数包中
template< typename T,typename ... List>
struct is_contained:std :: false_type {};

模板< typename T,typename ...列表>
struct is_contained< T,T,List ...> :std :: true_type {};

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

//这个类只能用允许的密钥创建
template< typename ... Keys>
class allow
{
public:
//检查是否允许passkey
template< typename Key>
allow(const passkey< Key>&)
{
static_assert(is_contained< Key,Keys> :: value,
不允许使用密钥。
}

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

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

//为quux函数创建一个密钥
PASSKEY_FUNCTION(quux_tag,void quux(int,double));

struct foo
{
void restricted1(allow< bar>){}
void restricted2(允许< 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不工作
/ * f.restricted1(passkey< ;巴兹>()); * /

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

struct qux
{
void run(void)
{
//自己的密钥不起作用,
//无法创建任何所需的密钥
/ * 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)
{
//不能使用quux的密钥
/ * f.restricted3(passkey< quux_tag>()) ; * /
}

int main(){}

请注意,在大多数情况下(所有非功能案例!)中,只需使用样板代码,无需特别定义。该代码通常并简单地实现了类和函数的任何组合的成语。



调用者不需要尝试创建或记住特定于该函数的密钥。相反,每个类现在都有自己唯一的密钥,该函数只是选择在密钥参数的模板参数中允许的密码(不需要额外的定义);这消除了这两个缺点。来电者只需创建自己的密钥和电话,并不需要担心任何事情。


Can we increase the re-usability for this key-oriented access-protection pattern:

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

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

To avoid continued misunderstandings, this pattern is different from the Attorney-Client idiom:

  • It can be more concise than Attorney-Client (as it doesn't involve proxying through a third class)
  • It can allow delegation of access rights
  • ... but its also more intrusive on the original class (one dummy parameter per method)

(A side-discussion developed in this question, thus i'm opening this question.)

解决方案

I like this idiom, and it has the potential to become much cleaner and more expressive.

In standard C++03, I think the following way is the easiest to use and most generic. (Not too much of an improvement, though. Mostly saves on repeating yourself.) Because template parameters cannot be friends, we have to use a macro to define passkey's:

// 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(){}

This method has two drawbacks: 1) the caller has to know the specific passkey it needs to create. While a simple naming scheme (function_key) basically eliminates it, it could still be one abstraction cleaner (and easier). 2) While it's not very difficult to use the macro can be seen as a bit ugly, requiring a block of passkey-definitions. However, improvements to these drawbacks cannot be made in C++03.


In C++0x, the idiom can reach its simplest and most expressive form. This is due to both variadic templates and allowing template parameters to be friends. (Note that MSVC pre-2010 allows template friend specifiers as an extension; therefore one can simulate this solution):

// 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(){}

Note with just the boilerplate code, in most cases (all non-function cases!) nothing more ever needs to be specially defined. This code generically and simply implements the idiom for any combination of classes and functions.

The caller doesn't need to try to create or remember a passkey specific to the function. Rather, each class now has its own unique passkey and the function simply chooses which passkey's it will allow in the template parameters of the passkey parameter (no extra definitions required); this eliminates both drawbacks. The caller just creates its own passkey and calls with that, and doesn't need to worry about anything else.

这篇关于我们可以增加这种面向密钥的访问保护模式的可重用性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆