Topic: Dynamic multimethod/variadic-type-cast container.
Author: Chris Gary <cgary512@gmail.com>
Date: Wed, 11 Feb 2015 02:01:15 -0800 (PST)
Raw View
------=_Part_1686_621816740.1423648875258
Content-Type: multipart/alternative;
boundary="----=_Part_1687_1218522878.1423648875258"
------=_Part_1687_1218522878.1423648875258
Content-Type: text/plain; charset=UTF-8
Before I venture to write something up, I wanted to run this by everyone.
I posted this before, but I wasn't happy with the title, formatting, lack
of clarity in some areas, and some mistakes.
*Scenario:*
A single, extensible descriptive layer wherein types describe only part of
an object's state.
Several concrete implementations that must extend, at runtime, objects of
descriptive types with relevant state (e.g. A graphics library where "image"
maps to "OpenGLTexture", "Direct3DTexture", or "GDIBitmap" opaquely).
Instances of descriptive types can be re-attached to different
implementations at runtime (e.g. switching renderers without re-loading
active resources from disk). Ergo, straightforward inheritance and
shared_ptr will not work.
A concrete implementation should also provide "best-fit" support for
user-defined types derived from those in the descriptive layer. It should
be possible to alter this support at runtime. Different instances of
implementations (e.g. different graphics context instances) may have
different support depending on plugin/extension selection (or anything,
really).
In other words: Extensible best-fit PIMPL.
*Solution:*
We need a type map! Not just a static type switch, but an unordered_map
analogue with the semantics of dynamic_cast over the set of target types.
Similar to multimethods, except variations are specified independently and
managed by containers at runtime (this is convenient for dynamic
plugin-based architectures).
*Definition Mockup:*
// Note: Omitting allocator details and
// assuming polymorphic types for now.
//
template<typename SignatureType>
class caster;
template<
typename ResultType,
typename ArgType, typename ...ArgTypes
>
class caster<ResultType(ArgType, ArgTypes...)>
{
public:
using signature = ResultType(ArgType, ArgTypes...);
template<typename ...OtherArgTypes>
enable_if<is_polymorphically_covariant<
ResultType(OtherArgTypes...),
signature
>, void> erase() // NO ARGUMENTS
{
// Lookup dispatch entry
// Erase if it exists
}
template<typename HandlerType>
enable_if<is_polymorphically_covariant<
signature_of<decay<HandlerType>>, signature
>, void> to(HandlerType &&handler)
{
// Lookup dispatch entry
// If it exists
// Throw bad_argument or something
// Allocate handler node, etc...
}
ResultType operator()(ArgType arg, ArgTypes...args)
{
// Note: Timing of key construction depends on
// the complexity of the inheritance hierarchy
// from which the cast starts searching.
//
// Lookup is always O(1) on average.
// This is unambiguous if matches are given
// preference in left-to-right order.
// In the monadic case, this is always unambiguous
// (somewhat like an open dynamic_cast;
// the first match wins).
// The casting logic obviously requires runtime support,
// and is somewhat slow in the variadic case due to
// ABI limitations.
// Lookup best-fitting dispatch entry
// (first by vptr memos, then table lookup)
// If it exists
// Invoke and return result
// Else
// Throw bad_cast
}
template<
typename HandlerType,
typename ...HanderTypes,
typename Enabler = enable_if<
conjunction<
is_polymorphically_covariant<
signature_of<decay<HandlerType>>, signature
>,
is_polymorphically_covariant<
signature_of<decay<HanderTypes>>, signature
>...
>, void>
>
>
caster(HandlerType &&handler, HanderTypes &&...handlers)
{
// Fill table.
// Note: We can do static collision
// checking here!
}
caster &operator = (const caster &other)
{// Copy contents
}
caster &operator = (caster &&other)
{// Clear and move other
}
caster(){}
caster(const caster &other){/*Copy contents*/}
caster(caster &&other){/*Direct move*/}
~caster() noexcept
{}
};
*Usage Mockup (monadic):*
class base{public:virtual ~base(){}};
class derived : public base{};
class left_derived : virtual public derived{};
class right_derived : virtual public derived{};
class most_derived : public left_derived, public right_derived{};
caster<int(base &)> c{
[](derived &){ // CASE 1
return 1;
},
[](right_derived &){ // CASE 2
return 2;
},
[](const left_derived &){ // CASE 3
return 3;
}
};
base b;
derived d;
left_derived ld;
right_derived rd;
most_derived md;
c(b); // bad_cast (nothing can accept only a "base &")
c(d); // calls CASE 1
c(ld); // calls CASE 3
c(md); // calls CASE 3 (left_derived matched first during BFS)
c(rd); // calls CASE 2
c.to([](base &){ // CASE 4
return 4;
});
c.to([](const base &){ // bad_argument: Entry already exists
return 4;
});
c(b); // calls CASE 4
c.erase<left_derived &>();
// erases CASE 3
c.erase<const left_derived &>();
// does nothing
// Good? Bad?
// I don't like the idea of iterators here...
promise<base &> p;
// Note: This assumes "then" can accept a
// callable with a simple value-type signature.
//
future<int> f = p.get_future().then(move(c));
p.set_value(md); // calls CASE 2 (right_derived matched)
auto y = f.get(); // y = 2
I have a prototype implementation, but I'm not able to publish the source
at the moment. I will put something together if there is sufficient
interest.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_1687_1218522878.1423648875258
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">Before I venture to write something up, I wanted to run th=
is by everyone.<br><br>I posted this before, but I wasn't happy with the ti=
tle, formatting, lack of clarity in some areas, and some mistakes.<br><br><=
b>Scenario:</b><br><br>A single, extensible descriptive layer wherein types=
describe only part of an object's state.<br><br>Several concrete implement=
ations that must extend, at runtime, objects of descriptive types with rele=
vant state (e.g. A graphics library where "<span style=3D"font-family: cour=
ier new,monospace;">image</span>" maps to "<span style=3D"font-family: cour=
ier new,monospace;">OpenGLTexture</span>", "<span style=3D"font-family: cou=
rier new,monospace;">Direct3DTexture</span>", or "<span style=3D"font-famil=
y: courier new,monospace;">GDIBitmap</span>" opaquely).<br><br>Instances of=
descriptive types can be re-attached to different implementations at runti=
me (e.g. switching renderers without re-loading active resources from disk)=
.. Ergo, straightforward inheritance and <span style=3D"font-family: courier=
new,monospace;">shared_ptr</span> will not work.<br><br>A concrete impleme=
ntation should also provide "best-fit" support for user-defined types deriv=
ed from those in the descriptive layer. It should be possible to alter this=
support at runtime. Different instances of implementations (e.g. different=
graphics context instances) may have different support depending on plugin=
/extension selection (or anything, really).<br><br>In other words: Extensib=
le best-fit PIMPL.<br><br><b>Solution:</b><br><br>We need a type map! Not j=
ust a static type switch, but an <span style=3D"font-family: courier new,mo=
nospace;">unordered_map</span> analogue with the semantics of <span style=
=3D"font-family: courier new,monospace;">dynamic_cast</span> over the set o=
f target types.<br><br>Similar to multimethods, except variations are speci=
fied independently and managed by containers at runtime (this is convenient=
for dynamic plugin-based architectures).<br><br><b>Definition Mockup:</b><=
br><br><span style=3D"font-family: courier new,monospace;"> &nbs=
p; // Note: Omitting allocator details and<br> // assumin=
g polymorphic types for now.<br> //<br> =
template<typename SignatureType><br> class caster;=
<br> <br> template<<br> &=
nbsp; typename ResultType,<br> &nb=
sp; typename ArgType, typename ...ArgTypes<br>  =
; ><br> class caster<ResultType(ArgType, ArgTypes..=
..)><br> {<br> public:<br> &nbs=
p;<br> using signature =3D Result=
Type(ArgType, ArgTypes...);<br> <br>&nb=
sp; template<typename ...OtherArgTyp=
es><br> enable_if<is_polymo=
rphically_covariant<<br> =
ResultType(OtherArgTypes...),<br>  =
; signature<br> =
>, void> erase() // NO ARG=
UMENTS<br> {<br>  =
; // Lookup dispatch entry<=
br> =
// Erase if it exists<br> &=
nbsp; }<br><br> template<=
;typename HandlerType><br> ena=
ble_if<is_polymorphically_covariant<<br>  =
; signature_of<decay<HandlerType&=
gt;>, signature<br> >, void=
> to(HandlerType &&handler)<br> &nb=
sp; {<br> =
// Lookup dispatch entry<br> &nbs=
p; // If it exists<br> =
; &n=
bsp; // Throw bad_argument or something<br>&n=
bsp;  =
; <br> &nb=
sp; // Allocate handler node, etc...<br>  =
; }<br><br> &nbs=
p; ResultType operator()(ArgType arg, ArgTypes...args)<br>  =
; {<br> &n=
bsp; // Note: Timing of key construction depends on=
<br> // the complex=
ity of the inheritance hierarchy<br> &=
nbsp; // from which the cast starts searching.<br> =
//<br> =
// Lookup is always O(1) on average.<br><br=
> // This=
is unambiguous if matches are given<br>  =
; // preference in left-to-right order.<br>&n=
bsp; <br> =
// In the monadic ca=
se, this is always unambiguous<br>  =
; // (somewhat like an open dynamic_cast;<br> =
// the first m=
atch wins).<br><br> &n=
bsp; // The casting logic obviously requires runtime support,<br>&nbs=
p; // and is so=
mewhat slow in the variadic case due to<br> &n=
bsp; // ABI limitations.<br><br> &=
nbsp; // Lookup best-fittin=
g dispatch entry<br> &=
nbsp; // (first by vptr memos, then table lookup)<br> &nbs=
p; =
// If it exists<br> &n=
bsp; // Invoke and re=
turn result<br> =
// Else<br> &nb=
sp; =
// Throw bad_cast<br> }<br=
><br> template<<br>  =
; typename HandlerTyp=
e,<br> ty=
pename ...HanderTypes,<br> &=
nbsp; typename Enabler =3D enable_if<<br> &=
nbsp; con=
junction<<br>  =
; is_polymorphically_=
covariant<<br> &nbs=
p; &=
nbsp; signature_of<decay<HandlerType>>, signature<br> &nbs=
p; &=
nbsp; >,<br> =
&nb=
sp; is_polymorphically_covariant<<br>  =
; &n=
bsp; signature_of<decay&=
lt;HanderTypes>>, signature<br> &n=
bsp;  =
; >...<br> &n=
bsp; >, void><br>&nbs=
p; ><br>&nbs=
p; ><br> &nbs=
p; caster(HandlerType &&handler, HanderTypes &&=
....handlers)<br> {<br>  =
; // Fill table.<br>&=
nbsp; <br>  =
; // Note: We can do =
static collision<br> &=
nbsp; // checking here!<br> =
}<br><br> caster &operator =
=3D (const caster &other)<br> =
{// Copy contents<br> }<br> =
; caster &operator =3D (caster &=
;&other)<br> {// Clear and mo=
ve other<br> }<br> &nb=
sp; <br> caster=
(){}<br> caster(const caster &=
;other){/*Copy contents*/}<br> ca=
ster(caster &&other){/*Direct move*/}<br> &n=
bsp; ~caster() noexcept<br> =
{}<br> };</span><br> <br><b>Usage Mock=
up (monadic):</b><br><br><span style=3D"font-family: courier new,monospace;=
"> class base{public:virtual ~base(){}};<br> &=
nbsp; class derived : public base{};<br> class left_deriv=
ed : virtual public derived{};<br> class right_derived : =
virtual public derived{};<br> class most_derived : public=
left_derived, public right_derived{};<br><br> caster<=
int(base &)> c{<br> [](der=
ived &){ // CASE =
1<br> ret=
urn 1;<br> },<br> &nbs=
p; [](right_derived &){ =
// CASE 2<br> &=
nbsp; return 2;<br> &n=
bsp; },<br> [](const left_derived=
&){ // CASE 3<br>  =
; return 3;<br> =
}<br> };<br><br> base b;<b=
r> derived d;<br> left_derived ld;<br>&=
nbsp; right_derived rd;<br> most_derived md;<=
br><br> c(b); // bad_cast (nothing can =
accept only a "base &")<br> c(d); /=
/ calls CASE 1<br> c(ld); // calls CASE=
3<br> c(md); // calls CASE 3 (left_der=
ived matched first during BFS)<br> c(rd); &nbs=
p; // calls CASE 2<br> <br> c.to([](base &=
;){ // CASE 4<br> &nbs=
p; return 4;<br> });<br> c.to([](const =
base &){ // bad_argument: Entry already exists<br>&nb=
sp; return 4;<br> });=
<br><br> c(b); // calls CASE 4<br> =
; <br> c.erase<left_derived &>();<b=
r> // erases CASE 3<br><br> c.erase<=
const left_derived &>();<br> // does nothing<br>&n=
bsp; // Good? Bad?<br> // I don't like the id=
ea of iterators here...<br><br> promise<base &>=
p;<br><br> // Note: This assumes "then" can accept a<br>=
// callable with a simple value-type signature.<br> =
; //<br> future<int> f =3D p.get_future=
().then(move(c));<br><br> p.set_value(md); &nb=
sp; // calls CASE 2 (right_derived matched)<br><br> auto =
y =3D f.get(); // y =3D 2</span><br><br>I have a prototyp=
e implementation, but I'm not able to publish the source at the moment. I w=
ill put something together if there is sufficient interest.<br> <br><b=
r></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_1687_1218522878.1423648875258--
------=_Part_1686_621816740.1423648875258--
.
Author: Chris Gary <cgary512@gmail.com>
Date: Wed, 11 Feb 2015 02:36:32 -0800 (PST)
Raw View
------=_Part_4721_1345437545.1423650992528
Content-Type: multipart/alternative;
boundary="----=_Part_4722_1913499552.1423650992528"
------=_Part_4722_1913499552.1423650992528
Content-Type: text/plain; charset=UTF-8
I would also like to add this is useful for things like message maps
(messages as types).
An interesting, and I think useful consequence of this is that polymorphic
messages must be substitutable.
I'm not a fan of integers, message ID enums, or string parsing when it
comes to extensibility.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_4722_1913499552.1423650992528
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">I would also like to add this is useful for things like me=
ssage maps (messages as types).<br><br>An interesting, and I think useful c=
onsequence of this is that polymorphic messages must be substitutable.<br><=
br>I'm not a fan of integers, message ID enums, or string parsing when it c=
omes to extensibility.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />
------=_Part_4722_1913499552.1423650992528--
------=_Part_4721_1345437545.1423650992528--
.