CallStream Part 3 : C++ Binding

paradigm-shift-2
A paradigm shift (or revolutionary science) is, according to Thomas Kuhn, in his influential book The Structure of Scientific Revolutions (1962), a change in the basic assumptions, or paradigms, within the ruling theory of science. It is in contrast to his idea of normal science.

CallStream Part 3 : C++ Binding

Good old C++ is one strongly typed language. CallStream concept is born (and bred) in the domain of type-less JavaScript. Also, C++ is (so called) static (compiled) language, vs dynamic (interpreted) JavaScript. C# is somewhere in between, (but not in the middle !) especially in it’s latest 4.0 reincarnation. Still I think CallStream should be implemented in C++ too. Why?

Because CallStream is not an implementation idiom of an programming language. It is a programming concept. And concept is not very good if it is not universally applicable. In which case it is not ubiquitous, and therefore not used everywhere by everyone. Not successful, in one word.

In case of C++, I have decided to make an (sort of a) type-less CallStream. An C++ interfacing mechanism which allows for more or less same call-streaming as in JavaScript. Where one can “call” anything with any arguments.

[sourcecode language=”cpp”] void main(int argc, wchar_t * argv[])
{
dbj::Functor & gf = dbj::GFunctor<int>();
dbj::CallStream & cs = dbj::CallStream() ;
/* One instance of a single CallStream type can be
used for a large variety of c++ calls */
cs ("add",1,2)
(dbj::ops::add<int>,3,5)
(dbj::ops::mul<int>,4,6)
(true)
(gf,6);
}
[/sourcecode]

Above is the main(), of the real code that compiles and runs. (in VisualStudio 2008). This CallStream implementation allows calls where first argument can be a string literal, function, intrinsic type or object (aka class instance). With any number of any type of arguments following. Let’s jump straight to the CallStream implementation.

[sourcecode language=”cpp” light=”false”] /*
CallStream is a single non-generic type. It declares overloaded generic function call operators
It depends on the existence of the family of ‘bridge’ functions
At compile time first appropriate ‘operator ()’ is found and used on the instance of this class
From inside ‘operator ()’ found, second an appropriate bridge is found and used
to the concrete implementation ‘behind’
if there is no required bridge function specialization found there is always a generic one.
Result is comprehensive decoupling between callers and implementations hidden behind a call stream.
*/
class CallStream {
public:
/*
generic ‘operator ()’ for pointers and variable number of arguments following
*/
template<typename T> const CallStream & operator ()(const T * f, …) const
{
va_list vl;
va_start( vl, f );
dbj::bridge( *f, vl );
va_end(vl);
return *this ;
}
/*
generic ‘operator ()’ for references and variable number of arguments following
*/
template<typename T> const CallStream & operator ()(const T & f, …) const
{
va_list vl;
va_start( vl, f );
dbj::bridge( f, vl );
va_end(vl);
return *this ;
}
/*
concretized ‘operator ()’ for string literals and variable number of arguments following
*/
template<> const CallStream & operator ()(const char * s, …) const
{
va_list vl;
va_start( vl, s );
dbj::bridge( s, vl );
va_end(vl);
return *this ;
}
} ;
[/sourcecode]

In this solution there is one CallStream class, serving the needs of all. How is that possible in C++ ?

The “trick” is in the generic function call operator. As implemented inside this non-generic class. Actually (at the moment) two templated “operator ()” overloads, and one specialization1, appear to be sufficient. Where each opearator (), has a simple task to passing the first argument and va_list pointer to the dbj::bridge() function. Where the real processing happens. The application space, that I am calling “behind”.

So, what is happening here ? Compiler dispatches all the calls from the main() example above to the CallStream instance, one of these three function call operators. Each of them calls one of the (potentially) many bridge functions available. The “bridge” in this context means the same as in the CallStream concept: an “bridge” to the implementation. I am also using the term “bridge” because this implementation hiding concept, reminds me (somewhat) on the “bridge pattern” as described by Gof4. Which bridge function is going to be called is decided by the c++ compiler, following the usual c++ rules of function overloading. A real “c++ kabala” when we mix in overloaded, with generic with specialized templated “bridges”.

Here is the generic bridge function and a handfull of basic specializations of it.

[sourcecode language=”cpp” light=”false”] namespace dbj {
/*
CallStream depends on the existence of this function.
it dispatches the calls received to the concretized bridge function as found by compiler.
if there is no required bridge function specialization found there is always a generic one.
this is the root of the whole family of bridge functions
*/
template<typename T> inline void bridge (const T & t_ , va_list & vl )
{
std::cout << "\nGeneric bridge used for type" << typeid(t_).name() ;
}
/*
Bridge specializations. If compiler does not find a specialized bridge it needs
it will call the generic bridge above.
The int bridge specialization
*/
template<> inline void bridge<int> (const int & j, va_list & /*vl*/)
{
std::cout << "\nGeneric bridge used for ‘int’ type, value: " << j ;
}
/* literal strings bridge */
inline void bridge (const char * s, va_list & /*vl*/ )
{
std::cout << "\nGeneric bridge used for ‘char *’ type, value: " << s ;
}
/* char’s bridge */
template<> inline void bridge<char> ( const char & c, va_list & /*vl*/)
{
std::cout << "\nGeneric bridge used for ‘char’ type, value: " << c ;
}
/* bool’s bridge */
template<> inline void bridge<bool> (const bool & b, va_list & /*vl*/)
{
std::cout << "\nGeneric bridge used for ‘bool’ type, value: " << b ;
}
} // dbj
[/sourcecode]

What we have till now is enough to call-stream in c++. All calls where first argument is not int, bool, char or string literal will be passed to the generic dbj::bridge.

[sourcecode language=”cpp”] // also note the variable number of arguments
dbj::CallStream & cs = dbj::CallStream() ;
cs ("add",1,2)
// goes to overloaded bridge for handling string literal
(‘X’,1,2,3)
// to specialized bridge for handling char
(7,6,8,9)
// to specialized bridge for handling int
(true,11)
// to specialized bridge for handling bool
(std::cout,"this","goes","to","generic","bridge");
// and we know where the above will go
[/sourcecode]

The last call above is passing c++ iostreams library std::cout object as first parameter to the call. Compiler realizes there is no bridge specialized for handling this type (std::ostream), and thus uses generic bridge.

Detour: Better design?

One could imagine “OO solution” based om inheritance, where only one generic bridge is used which somehow “cleverly” dispatches calls to their final destinations. That would inevitably require looking into the concrete type of the first call parameter, thus quite slow and cumbersome. Also an solution not very resilient to change. It will be in essence one huge switch to be frequently updated as new types have to be handled by the CallStream. And of course, this single generic bridge+dispatcher will be very difficult to maintain and re-use as a library solution. It is much better to use c++ compiler to find your specialized bridge, that you have “built” (inside your own code) so that CallStream can pass the appropriate calls to your particular implementation you want to place “behind” a CallStream mechanism.

This is all solvable as above with templates and specializations.  For an “classical OO” solution, consider this :

[sourcecode language=”cpp”] namespace lib {
/* Alternative design: this is an abstract base of all CallStreams */
class CallStreamBase {
public:
/* ‘operator ()’ for type pointer*/
template<typename T> const CallStreamBase & operator ()(const T * f, …) const = 0 ;
/* ‘operator ()’ for type reference */
template<typename T> const CallStreamBase & operator ()(const T & f, …) const = 0 ;
};
}
[/sourcecode]

We can easily imagine solution based on hierarchy of cocnrete CallStreams. Which will born much more c++ code, and again a solution which will require inheriting from the core mechanism for each new type to be handled. A classical c++ nightmare where there is a huge logical hierarchy, physically scattered over many applications , with a root of it being in some single library. The root which therefore has to be “Cast in blood and signed in stone” never ever to be changed. Also above there is no implementation, only required behaviour. Solutions as the one above do generate redundant implementations “all over the place”. I am glad to notice that once more inheritance is rendered as not good, for an solution to be used in real life situations.

Back to Better

 

Now, back to our shiny solution based on single CallStream mechanism and generic bridge specialized by “customers” for their specific purposes.

Imagine we have a customer which wants to hide her complex “things” behind a CallStream, and thus make her solution look nice and easy and eminently usable. Accidentally the same customer is keen on functors, How would that customer quickly subscribe to CallStream concept and implement a small layer of code necessary ?

First, a simple functor has to be invented to act as a primary type passed into call streams and used to create a bridge specialization that will be used by c++ compiler , to pass the calls and parameters to the code “behind”.

[sourcecode language=”cpp” light=”false”] /*Abstract base to all functors specific to this example */
class Functor {
protected std::string tag_ ;
public:
/* in this example each functor inheritor must have an ‘operator (va_list &)’ implemented */
virtual void operator ()( va_list & vl ) const = 0 ;
/* method to show functor type and value held using std::cout by default */
virtual void show ( std::ostream & out_ = std::cout ) const = 0 ;
};
/* The CallStream bridge specialization that will reach to any kind-of-a Functor above */
template<> inline void bridge<Functor> (const Functor & gf, va_list & vl)
{
std::cout << std::endl << "Bridge to the " << typeid(gf).name();
gf(vl);
// passing to the functor variable number of arguments
gf.show() ;
// showinf the type and value held
}
[/sourcecode]

CallStream need not be changed for this customer to use it and it’s specialized bridge to be found and used. Also in this example everything is in the same namespace (dbj), while in reality Functor above will be in separate namespace. Again we could imagine and present a solution where Functor will not deal with va_list’s. That would require more complex bridge to decode the parameters etc. Instead I have made a solution with templatized functors , who “know” what type and value they are dealing with. For example:

[sourcecode language=”cpp” light=”false”] /*The generic functor which deals with different types of arguments sent through call stream.*/
template<typename T> class GFunctor : public Functor
{
public:
typedef T ValueType;
typedef GFunctor<T> MyType ;
typedef GFunctor<T> * MyTypePtr ;
protected :
T value_ ;
MyType & This () const { return * const_cast<MyTypePtr>(this);}
public:
GFunctor( const char * tag ) {
if ( tag ) This().tag_ = strdup(tag) ; else This().tag_ = "" ;
}
/*This functor requires exactly one argument of type T*/
virtual void operator ()( va_list & vl ) const {
This().value_ = va_arg( vl, T );
}
virtual void show ( std::ostream & out_ = std::cout ) const {
out_ << "\nFunctor of type " << typeid(*this).name()
<< ", is holding the type " << typeid(T).name()
<< ", and the value is: " << value_ ;
}
};
[/sourcecode]

Since customer is using functors here, we could pass some useful data inside functors instances too.

[sourcecode language=”cpp” light=”false”] const dbj::Functor & gf = dbj::GFunctor<int>("LABEL");
/* make a functor to deal with single int’s */
const dbj::CallStream & cs = dbj::CallStream() ;
/*Again the same single CallStream is used */
cs (gf,9)
// "LABEL" and 9 are available to the implementation
(gf,6);
// "LABEL" and 6
[/sourcecode]

But now we are encroaching into the customers domain. The code that was necessary to use (or be used by) CallStream is short and sweet. And it is perfectly applicable for anyone using functors with CallStream.

One more example and that is it. This “post” is already turning into a “booklet” 😉 This time I will pay attention to CallStream customers who are in the same time perhaps implementing some library in the (right) spirit of c++ std : a lot of generic functions and the whole solution in header files. Still they want to have an “CallStream enabled” interfacing to their library. Let’s keep this one simple but still valid c++.

[sourcecode language=”cpp” light=”false”] /* the whole ‘ops’ library is made out of inline
generic functions and no specializations */
namespace ops {
/* generic functions to add or multiply "anything" that has binary ‘+’ and ‘*’ operators */
template<typename T> inline T add( const T & a, const T & b) { return a + b ; }
template<typename T> inline T mul( const T & a, const T & b) { return a * b ; }
/* int_int_int is a function pointer type to a concretized form of generic ops above
which are concretized with an int type
*/
typedef int ( * int_int_int ) ( const int &, const int & ) ;
}
[/sourcecode]

This is the whole library. And here is the code required so that CallStream users can use it.

[sourcecode language=”cpp” light=”false”] namespace dbj {
/*This bridge will be called for all the ops generic functions which are
concretized as dealing with int’s
CallStream need not be changed for this bridge to be found and used.
*/
inline void bridge (const ops::int_int_int operation, va_list & vl )
{
int op1 = va_arg( vl, int );
int op2 = va_arg( vl, int );
int rez = operation(op1,op2) ;
std::cout << std::endl << "Bridge to the " << typeid(operation).name()
<< " result of operation(" << op1 << "," << op2 << ") is : " << rez ;
}
} // namespace dbj
[/sourcecode]

Yes, just this one specialized bridge is enough for the CallStream mechanism to find it and to pass calls to the “ops” library operations.

[sourcecode language=”cpp” light=”false”] void main(int /*argc*/, wchar_t * /*argv*/[])
{
const dbj::CallStream & cs = dbj::CallStream() ;
cs (ops::add<int>,3,5)
(ops::mul<int>,4,6)
(ops::add<float>,2.3,7.12) ; // works but generic bridge is called
}
[/sourcecode]

Also, just to confirm the CallStream malleability, if one passes ‘ops’ operation which is not having it’s bridge yet , the code will still work, it is only that generic bridge will be called. Of course the CallStream instance above is valid for passing to it any calls. It is the same as before. It will find any specialized bridge presented here, to pass this calls to heir intended implementations.

Conclusion
All this C++ “snazzy” idioms, are not here because of my “requirements” or dictate. I am showing all these used with/by CallStream to confirm the eligibility of the whole concept for the C++ programming too.


1Or template concretization, as I am calling it. And a few others, too.