我目前正在玩 mixin 层设计,并且我被困住了
烦人的问题。
让我们考虑以下基本 mixin 层:
template <typename Next>
struct Layer1 : public Next
{
struct A : public Next::A
{
void f() { g(); }
void g() {}
};
};
这里没有什么花哨的,只是一个带有 2 个方法 f()
和 g()
方法的简单 mixin。
请注意,来自 f()
的 g()
调用静态绑定到此
具体的Layer1::A::g()
。
现在,我想要的是能够完全挂钩这个方法
mixin 来实现,比如说,一个日志层:
template <typename Next>
struct Layer2 : public Next
{
struct A : public Next::A
{
void f()
{
std::cout << "Layer2::A::f() [enter]" << std::endl;
Next::A::f();
std::cout << "Layer2::A::f() [leave]" << std::endl;
}
void g()
{
std::cout << "Layer2::A::g() [enter]" << std::endl;
Next::A::g();
std::cout << "Layer2::A::g() [leave]" << std::endl;
}
};
};
考虑到Layer2>
,这里的问题是任何调用
Layer2
之上的层中的 f()
和 g()
将正确向下级联到
Layer2::A::g()
,从而显示正确的日志消息。但
从 Layer2
下面调用 f()
和 g()
将不会记录任何内容,因为
该调用将静态绑定到可用的 g()
拨打电话的时间。
这意味着从 Layer2
之上的任何层调用 f()
显然会
仍然总是从 Layer1::A::f()
调用 Layer1::A::g()
并且不显示
日志消息。
我想出了两个解决这个问题的方法:
-
虚拟性:显然不可接受。 mixin 层的要点
就是在没有必要的时候避免虚拟。
-
向图层添加模板参数以提供先前的
层,类似的东西。
。
template <typename Next, template <typename> class Prev>
struct Layer2 : public Next
{
typedef Next next_t;
struct A : public Next::A
{
void f()
{
std::cout << "Layer2::A::f() [enter]" << std::endl;
Next::A::f();
std::cout << "Layer2::A::f() [leave]" << std::endl;
}
void g()
{
std::cout << "Layer2::A::g() [enter]" << std::endl;
Next::A::g();
std::cout << "Layer2::A::g() [leave]" << std::endl;
}
};
};
template <typename Next, template <typename> class Prev>
struct Layer1 : public Next
{
typedef Next next_t;
struct A : public Next::A
{
void f()
{
std::cout << "Layer1::A::f() [enter]" << std::endl;
((typename Prev<Layer1<Next,Prev> >::A*)this)->g();
std::cout << "Layer1::A::f() [leave]" << std::endl;
}
void g()
{
std::cout << "Layer1::A::g() [enter]" << std::endl;
std::cout << "Layer1::A::g() [leave]" << std::endl;
}
};
};
typedef Layer2<Layer1<Layer0,Layer2>,NullType> Application;
嗯,它有效,但我想隐藏第二个模板
参数,因为它是多余的。
想问一下大家有没有遇到过这样的问题,是怎么解决的
由于明显缺乏,您是否开发了解决方案来解决它
有关 mixin 层的文章。
I'm currently playing with mixin layers designs, and I'm stuck with an
annoying problem.
Let's consider the following basic mixin layer:
template <typename Next>
struct Layer1 : public Next
{
struct A : public Next::A
{
void f() { g(); }
void g() {}
};
};
Nothing fancy here, just a simple mixin with 2 methods f()
and g()
.
Notice that the g()
call from f()
is is statically binded to this
specific Layer1::A::g()
.
Now, what I want is to be able to completely hook the methods of this
mixin to implement, say, a logging layer:
template <typename Next>
struct Layer2 : public Next
{
struct A : public Next::A
{
void f()
{
std::cout << "Layer2::A::f() [enter]" << std::endl;
Next::A::f();
std::cout << "Layer2::A::f() [leave]" << std::endl;
}
void g()
{
std::cout << "Layer2::A::g() [enter]" << std::endl;
Next::A::g();
std::cout << "Layer2::A::g() [leave]" << std::endl;
}
};
};
Considering Layer2<Layer1<...>>
, the problem here is that any call of
f()
and g()
from a layer above Layer2
will properly cascade down to
the Layer2::A::g()
, and thus display the proper logging messages. But
any call of f()
and g()
from below Layer2
will not log anything since
the call would have been statically binded to the g()
available at the
time the call was made.
This means that calling f()
from any layer above Layer2
will obviously
still always call Layer1::A::g()
from Layer1::A::f()
and not display
the logging messages.
I came up with 2 solutions to this problem:
-
Virtuality: clearly not acceptable. The whole point of mixin layers
is to avoid virtuality when not necessary.
-
Adding a template parameter to the layers to provide the previous
layer, something of the kind.
.
template <typename Next, template <typename> class Prev>
struct Layer2 : public Next
{
typedef Next next_t;
struct A : public Next::A
{
void f()
{
std::cout << "Layer2::A::f() [enter]" << std::endl;
Next::A::f();
std::cout << "Layer2::A::f() [leave]" << std::endl;
}
void g()
{
std::cout << "Layer2::A::g() [enter]" << std::endl;
Next::A::g();
std::cout << "Layer2::A::g() [leave]" << std::endl;
}
};
};
template <typename Next, template <typename> class Prev>
struct Layer1 : public Next
{
typedef Next next_t;
struct A : public Next::A
{
void f()
{
std::cout << "Layer1::A::f() [enter]" << std::endl;
((typename Prev<Layer1<Next,Prev> >::A*)this)->g();
std::cout << "Layer1::A::f() [leave]" << std::endl;
}
void g()
{
std::cout << "Layer1::A::g() [enter]" << std::endl;
std::cout << "Layer1::A::g() [leave]" << std::endl;
}
};
};
typedef Layer2<Layer1<Layer0,Layer2>,NullType> Application;
Well, it works, but I would like to hide this second template
parameter since it is redundant.
I wondered if any of you ever encountered such problem, and what
solutions did you developped to solve it, since there is a clear lack
of articles on mixin layers.
发布评论