PHP DataGrid v.1.x.x





--------------------------------------------------------------
To download version 1.1.1 click here.
This version changes:
 - fixed bugs in getHeaderName()
 - fixed bugs in noDataFound()
 - fixed bugs in setColumnsInViewMode()
 - fixed bugs in combine_url()
 - fixed bugs in setCssClass()

To download version 1.1.0 click here.



--------------------------------------------------------------
PHP DataGrid document for version 1.x.x 
--------------------------------------------------------------

1. System Requirements
------------------------------
PHP DataGrid is operating system independent. You get it works on both Linux and 
Windows. You need following components are installed:

- PHP 4.0 script engine or later 
- Apache 1.3 or above 
- mySQL 3.23 or above 

2. Installation 
------------------------------
<? include("$PHP_DATAGRID_FILE_PATH/datagrid.class") ?>
Where $PHP_DATAGRID_FILE_PATH - is a file path where PHPDataGrid stored on your server.

You may also use require() PHP function instead of include().

3. Features
------------------------------
- View mode
- Columns sorting
- Paging 
  -- current/total pages selector
  -- pager
  -- page size drop-down box
- embedded css templates

To download version 1.1.1 click here.

Examples of code PHP DataGrid v.1.x.x


In datagrid.class file you can find commented lines of how to create, call and work with this class. 

All you need is to copy all commented lines of the code into you page and uncomment some lines. That's all!

Now we'll check this example to get you understanding how it works.
For example, we have a database (mySQL) with 3 tables:

country 
   CountryID
   RegionID
   Name
   Description
   Population
   PictureURL
   democraty
regions
   RegionID
   Name
democracy
   did
   description

// creating variables that we need for database connection 
$DB_USER='root'; // often like this: prefix_name
$DB_PASS=''; // must be already enscrypted
$DB_HOST='localhost'; // often localhost
$DB_NAME='thebutto_test'; // often like this: prefix_name

ob_start(); 
// now we create connection with our database
$db_conn=mysql_connect($DB_HOST,$DB_USER,$DB_PASS);mysql_select_db($DB_NAME,$db_conn);

// create sql statment (any SELECT statment you want)
// it's recomended to put Primary Key at the first place (CountryID in this example)
$sql="SELECT ".
" countries.CountryID, ".
" countries.Name, ".
" regions.Name as Region, ".
" countries.Description, ".
" countries.Population, ".
" countries.PictureURL, ".
" democraty.description as isdemocraty ".
" FROM countries ".
" INNER JOIN regions ON countries.regionID=regions.regionID ".
" LEFT OUTER JOIN democraty ON countries.democraty=democraty.did ";

// creating a new instance of DataGrid class
$dgrid = new DataGrid();

// linking data source with DataGrid
$dgrid->dataSourse($db_conn, $sql); 

// General Settings
// ---------
/*** here we give unique names for HTML elements */
$dgrid->setUniqueNames("_abc"); 
/*** wich layout we want: 0-tabular(default), 1-columnar */
$dgrid->setLayouts(array("view"=>0));
/*** set for all columns 'nowrap' parameter */
$dgrid->setColumnsNoWrap("nowrap");
/*** set css class, default: "default" */
$dgrid->setCssClass("default");

// Sorting & Paging Settings: 
// ---------
/*** allow sorting on columns: true/false; default - true */
$dgrid->allowSorting(true); 
/*** allow paging option: true/false; default - true */
$dgrid->allowPaging(true); 
/*** set paging settings: on top & bottom 
$dgrid->setPagingSettings(
array("results"=>true, "results_align"=>"left",
"pages"=>true, "pages_align"=>"center",
"page_size"=>true, "page_size_align"=>"right"),
array()
);


// View Mode Settings: 
// ---------
/*** set view mode settings: */
$dgrid->setColumnsInViewMode(array(
"Name" =>array("header"=>"Country","type"=>"label"),
"Region" =>array("header"=>"Region","type"=>"label"),
"PictureURL" =>array("header"=>"Image","type"=>"link",
"href"=>"http://www.yahoo.com", "target"=>"_new"),
"isdemocraty" =>array("header"=>"Is Democraty","type"=>"label")
));

/*** set debug mode = true/false(default) $ messaging = true(default)/false */
$dgrid->bind(false, false); 
ob_end_flush();

Now, let's see what we've got:


PHP DataGrid Version Comparison

This is a general version comparison for PHP DataGrid class: 

Ver. 1.x.xVer. 2.x.xVer. 3.x.xVer. 4.x.x
CSS emb.templatesYesYesYesYes
Column sortingYesYesYesYes
FilteringNoYesYesYes
PaggingYesYesYesYes
ExportingNoNoNoYes
PrintingNoNoNoYes
Automatic validationNoNoYesYes
-- Client sideNoNoYesYes
-- Server sideNoNoNoNo
View modeYesYesYesYes
-- Tabular layoutYesYesYesYes
-- Columnar layoutYesYesYesYes
Details modeNoYesYesYes
-- Columnar layoutNoYesYesYes
Add new modeNoNoYesYes
-- Tabular layoutNoNoYesYes
-- Columnar layoutNoNoYesYes
Edit modeNoNoYesYes
-- Tabular layoutNoNoYesYes
-- Columnar layoutNoNoYesYes
Delete modeNoNoYesYes
Multi-Database supportNoNoNoYes
Multi-Language supportNoNoNoYes
Multi-Browser supportNoNoNoYes
W3C CSS validationNoNoNoYes
WYSIWYG editorNoNoNoYes

Getting Started PHP

PHP DataGrid - Step by Step.

Step 1.
+---------------------------------------------------------------------------+
| Creating & Calling:
+---------------------------------------------------------------------------+

Be sure you write here a true path to the datagrid.class.php file, relatively to this file.

require_once('datagrid/datagrid.class.php');


Write true values to these variable. Be sure you use a prefix if you need it. 

## *** creating variables that we need for database connection 

$DB_USER='name'; /* usually like this: prefix_name */
$DB_PASS=''; /* must be already enscrypted (recommended) */
$DB_HOST='localhost'; /* usually localhost */
$DB_NAME='dbName'; /* usually like this: prefix_dbName */


First of all we need to be connected to database.

$db_conn = mysql_connect($DB_HOST,$DB_USER,$DB_PASS);
mysql_select_db($DB_NAME,$db_conn);


Now you have to prepare SELECT SQL statement. It can be any type of SELECT statement your database supports (with JOIN, UNION etc.), but you must put the primary key on the first place. Also be careful to write all fileds you need to be shown, because the DataGrid class works with only fields you placed in SELECT statement.Don't add here ORDER BY, LIMIT words or ; at the end of the statement.

## *** put a primary key on the first place 

$sql = "SELECT primary_key, filed_1, filed_2 ... FROM tableName ";


Creating the new class instance and linking the DataGrid class to our database.

$debug_mode = false;
$messaging = true;
$dgrid = new DataGrid($debug_mode, $messaging);
$dgrid->dataSource($db_conn, $sql);



Step 2.
+---------------------------------------------------------------------------+
| General Settings:
+---------------------------------------------------------------------------+

We can add an unique prefix (optional) to our datagrid if we want to prevent using of double names on this page (in case, when you use some datagrids or forms on one page)

## *** set unique prefix 

$u_prefix = "_abc";
$dgrid->setUniqueNames($u_prefix);


If you want to use a local language (not English) - you have to set the right 
encoding. The properly fields in your database must be created with the same CHARACTER SET.

## *** set encoding (default - utf8) 

$dg_encoding = "utf8";
$dgrid->setEncoding($dg_encoding);


Option for some right-to-left languages (Hebrew, Arabic etc.)

## *** set direction: "ltr" or "rtr" (default - ltr) 

$direction = "ltr";
$dgrid->setDirection($direction);


Set layouts for datagrid in view mode, edit mode and for the filtering block

## *** set layouts: 0 - tabular(horizontal) - default, 1 - columnar(vertical) 

$layouts = array("view"=>0, "edit"=>1, filter=1);
$dgrid->setLayouts($layouts);


Set various modes for datagrid. 
True - allow the operation in this mode("view" or "edit"), false - don't allow.
Type - a type of command button (link, html button or image).
byFieldValue - if you want to make this field to be a link to edit mode page (instead of edit button), write here a name of the field. If you want to use a standart edit command button leave it empty - "byFieldValue"=>""

## *** set other modes ("type" = "link|button|image"), ("byFieldValue" - make the field as a link to edit mode page)
$modes = array(
 "add"=>array("view"=>true, "edit"=>false, "type"=>"link"),
 "edit"=>array("view"=>true, "edit"=>true, "type"=>="link",  "byFieldValue"=>"FieldName"),
 "cancel"=>array("view"=>true, "edit"=>true, "type"=>="link"),
 "details"=>array("view"=>true, "edit"=>false, "type"=>="link"),
 "delete"=>array("view"=>true, "edit"=>true, "type"=>="image")
);
$dgrid->setModes($modes);


Set "nowrap" attribute for all columns in the datagrid.

## *** set nowrap attribute for all columns

$wrap = "nowrap";
$dgrid->setColumnsNoWrap($wrap);


Set CSS parameters for the datagrid. If you want to use embedded css class - define $css_type as "embedded" and $css_class as you wish. If you use external file of CSS styles - you define $css_type as "file" and $css_class as full path to your file. For example: $css_class = "../css/datagrid_style.css". Embedded CSS styles: "default", "gray", "like adwords" and "salomon".

## *** set CSS class for datagrid: 

$css_class = "default";
$css_type = "embedded";
$dgrid->setCssClass($css_class, $css_type);


To be continued...


Step 3.
+---------------------------------------------------------------------------+
| Printing & Exporting Settings:
+---------------------------------------------------------------------------+

Set printing as true, if you want to allow this option

## *** set printing option: true(default) or false 

$printing_option = true;
$dgrid->allowPrinting($printing_option); 


Set exporting as true, if you want to allow this option

## *** set exporting option: true(default) or false 

$exporting_option = true;
$dgrid->allowExporting($exporting_option);



Step 4.
+---------------------------------------------------------------------------+
| Sorting & Paging Settings:
+---------------------------------------------------------------------------+

Set sorting option as true, if you want to allow sorting on columns

## *** set sorting option: true(default) or false 

$$sorting_option = true;
$dgrid->allowSorting($$sorting_option); 


Set paging option as true, if you want to allow paging on datagrid

## *** set paging option: true(default) or false 

$paging_option = true;
$dgrid->allowPaging($paging_option);


Set aditional paging settings. $bottom_paging or $top_paging are defines both (top and bottom) paging behaviour. We have thee part of paging line: results, pages and page_size dropdownbox. You need to set parameters for each of them. If you don't want to show any of them - leave it empty (Ex.: $bottom_paging = array()). If you want to define your own dropdown box with page sizes - you can make it in $pages_array array - see example below. Also you need to define default page size in $default_page_size variable.

## *** set paging settings 

$bottom_paging = array("results"=>true, "results_align"=>"left", "pages"=>true, "pages_align"=>"center", "page_size"=>true, "page_size_align"=>"right");
$top_paging = array("results"=>true, "results_align"=>"left", "pages"=>true, "pages_align"=>"center", "page_size"=>true, "page_size_align"=>"right");
$pages_array = array(10, 25, 50, 100, 250, 500, 1000);
$default_page_size = 10;
$dgrid->setPagingSettings($bottom_paging, $top_paging, $pages_array, $default_page_size);



Step 5.
+---------------------------------------------------------------------------+
| 5. Filter Settings:
+---------------------------------------------------------------------------+

If you want to allow filtering mode, set $filtering_option as true. 

## *** set filtering option: true or false(default)

$filtering_option = true;
$dgrid->allowFiltering($filtering_option);


To be continued...


Step 5.
+---------------------------------------------------------------------------+
| 6. View Mode Settings:
+---------------------------------------------------------------------------+

This method sets up columns, that will be viewable. 
"header"=>"..." - name of the column header 
"type"=>"..." - type of column: label, image, linktoview, link (http://, mailto:) or password
"align"=>"..." - alignment of the column data (left or right)
"wrap"=>"..." - wraping of the column data (wrap or nowrap)
"text_length"=>"..." - viewable text length (any number, "-1" - don't truncate )
"case"=>"..." - text case (normal or upper or lower)

## *** set columns in view mode

$vm_colimns = array(
"FieldName_1"=>array("header"=>"Name_A", "type"=>"label", "align"=>"left", "wrap"=>"wrap|nowrap", "text_length"=>"-1", "case"=>"normal|upper|lower"),
"FieldName_2"=>array("header"=>"Name_B", "type"=>"image", "align"=>"left", "wrap"=>"wrap|nowrap", "text_length"=>'-1', "case"=>"normal|upper|lower"),
"FieldName_3"=>array("header"=>"Name_C", "type"=>"linktoview", "align"=>"left", "wrap"=>"wrap|nowrap", "text_length"=>'-1', "case"=>"normal|upper|lower"),
"FieldName_4"=>array("header"=>"Name_D", "type"=>"link", "align"=>"left", "wrap"=>"wrap|nowrap", "href"=>"www.yahoo.com", "target"=>"_new", "text_length"=>"-1", "case"=>"normal|upper|lower"),
"FieldName_5"=>array("header"=>"Name_E", "type"=>"link", "align"=>"left", "wrap"=>"wrap|nowrap", "field"=>"field_name", "prefix"=>"http://", "target"=>"_new", "text_length"=>"-1", "case"=>"normal|upper|lower"),
"FieldName_6"=>array("header"=>"Name_F", "type"=>"password", "align"=>"left", "wrap"=>"wrap|nowrap", "text_length"=>"-1", "case"=>"normal|upper|lower")
);
$dgrid->setColumnsInViewMode($vm_colimns);


To be continued...

Installation

PHP DataGrid - Step by Step.


NEW INSTALLATION (for version 3.x.x or above)
========================================================

Step 1.
--------------------------------------------------------

Upload the code_template.php file and folder named "datagrid" (with all files it includes) to your document root (public_html, www, htdocs etc.) using FTP. 

For example:
public_html/your_site/code_template.php
public_html/your_site/datagrid


Step 2.
--------------------------------------------------------

Using phpMyAdmin or another tool, create your database and user, 
and assign that user to the database. Write down the name of the database, 
username, and password for this database for the database installation 
procedure. 

Create all appropriate database tables.

Step 3.
--------------------------------------------------------

Open code_template.php file, uncomment these 4 lines and change variable 
values on yours (saved on step 3).


// $DB_USER='name'; /* usually like this: prefix_name */
// $DB_PASS=''; /* must be already enscrypted (recommended) */
// $DB_HOST='localhost'; /* usually localhost */
// $DB_NAME='dbName'; /* usually like this: prefix_dbName */


Step 4.
--------------------------------------------------------

Rename code_template.php file according to your needs.

Congradulations, you have PHP DataGrid Installed now!

Covariance and Contravariance in C++ Standard Library

Covariance and Contravariance are concepts that come up often as you go deeper into generic programming. While designing a language that supports parametric polymorphism (e.g., templates in C++, generics in Java, C#), the language designer has a choice between Invariance, Covariance, and Contravariance when dealing with generic types. C++'s choice is "invariance". Let's look at an example.
struct Vehicle {};
struct Car : Vehicle {};

std::vector<Vehicle *> vehicles;
std::vector<Car *> cars;

vehicles = cars; // Does not compile
The above program does not compile because C++ templates are invariant. Of course, each time a C++ template is instantiated, the compiler creates a brand new type that uniquely represents that instantiation. Any other type to the same template creates another unique type that has nothing to do with the earlier one. Any two unrelated user-defined types in C++ can't be assigned to each-other by default. You have to provide a copy-constructor or an assignment operator.

However, the fun starts when you realize that it's just a choice and there are other valid choices. In fact, C++ makes a different choice for pointers and references. For example, it's common knowledge that pointer of type Car is assignable to pointer of type Vehicle. That's because Car is a subtype of Vehicle. More accurately, the Car struct inherits from the Vehicle struct and the compiler allows us to use Car pointers in places where Vehicle pointer is expected. I.e., subtyping is activated through inheritance. Later in the post we will use subtyping without using inheritance.

If you think about pointers as a shortcut for the Pointer template below, it becomes apparent that the language has some special rules for them. Don't let the special * syntax confuse you. It is just a shortcut to avoid the ceremony below.
template <class T>
using Pointer = T *;

Pointer<Vehicle> vehicle;
Pointer<Car> car;

vehicle = car; // Works!
So what choices are available? The question we want to ask ourselves is, "What relationship do I expect between instantiations of a template with two different types that happen to have a subtype relationship?"
  • The first choice is no relationship. I.e., the template instantiations completely ignore the relationship between parameter types. This is C++ default. It's called invariance. (a.k.a. C++ templates are invariant)
  • The second choice is covariant. I.e., the template instantiations have the same subtype relationship as the parameter types. This is seen in C++ pointers and also in std::shared_ptr, std::unique_ptr because they want to behave as much like pointers as possible. You have write special code to enable that because the language does not give it to you by default.
  • The third choice is contravariance. I.e., the template instantiations have the opposite subtype relationship to that of the parameter types. I.e., TEMPLATE<base> is subtype of TEMPLATE<derived>. We'll come back to contravariance in much more detail later in the post.
All C++ standard library containers are invariant (even if they contain pointers).

Covariance

As said earlier, with covariance, the templated type maintains the relationship between argument types. I.e., if argument types are unrelated, the templated types shall be unrelated. If derived is a sub-type of base (expressed as inheritance) then TEMPLATE<derived> shall be sub-type of TEMPLATE<base>. I.e., any place where TEMPLATE<base> is expected, TEMPLATE<derived> can be substituted and everything will work just fine. The other way around is not allowed.

There are some common examples of covariance in C++ standard library.
std::shared_ptr<Vehicle> shptr_vehicle;
std::shared_ptr<Car> shptr_car;
shptr_vehicle = shptr_car; // Works
shptr_car = shptr_vehicle' // Does not work.

std::unique_ptr<Vehicle> unique_vehicle;
std::unique_ptr<Car> unique_car;
unique_vehicle = std::move(unique_car); // Works
unique_car = std::move(unique_vehicle); // Does not work
One (formal) way to think about covariance is that "the type is allowed to get bigger upon assignment". I.e., Vehicle is broader/bigger type than Car. Here's a quick rundown of some of the commonly used C++ standard library types and their covariance/contravariance properties.

TypeCovariantContravariant
STL containersNoNo
std::initializer_list<T *>NoNo
std::future<T>NoNo
boost::optional<T>No (see note below)No
std::shared_ptr<T>YesNo
std::unique_ptr<T>YesNo
std::pair<T *, U *>YesNo
std::tuple<T *, U *>YesNo
std::atomic<T *>YesNo
std::function<R *(T *)>Yes (in return)Yes (in arguments)

The boost::optional<T> appears to be covariant but it really isn't because it slices the object underneath. The same thing happens with std::pair and std::tuple. Therefore, they behave covariantly correctly only when the parameter type itself behaves covariantly.

Finally, Combining one covariant type with another (e.g., std::shared_ptr<std::tuple<T *>>) does not necessarily preserve covariance because it is not built into the language. It is often implemented as a single-level direct convertibility. I.e., std::tuple<Car *> * is not directly convertible to std::tuple<Vehicle *> *. It would have been if the language itself enforced subtyping between std::tuple<Car*> and std::tuple<Vehicle *> but it does not. On the other hand, std::tuple<std::shared_ptr<T>> behaves covariantly.

By "single-level direct convertibility", I mean the following conversion of U* to T*. Convertibility is poor man's test for subtyping in C++.

A covariant SmartPointer might be implemented as follows.

template <class T>
class SmartPointer
{
public:
    template <typename U>
    SmartPointer(U* p) : p_(p) {}

    template <typename U>
    SmartPointer(const SmartPointer<U>& sp,
                 typename std::enable_if<std::is_convertible<U*, T*>::value, void>::type * = 0) 
      : p_(sp.p_) {}

    template <typename U>
    typename std::enable_if<std::is_convertible<U*, T*>::value, SmartPointer<T>&>::type 
    operator=(const SmartPointer<U> & sp)
    {
        p_ = sp.p_;
        return *this;
    }

   T* p_;
};

Contravariance

Contravariance, as it turns out, is quite counter-intuitive and messes up with your brain. But it is a very valid choice when it comes to selecting how generic types behave. Before we deal with contravariance, lets quickly revisit a very old C++ feature: covariant return types.

Consider the following class hierarchy.
class VehicleFactory {
  public:
    virtual Vehicle * create() const { return new Vehicle(); }
    virtual ~VehicleFactory() {}
};

class CarFactory : public VehicleFactory {
public:
    virtual Car * create() const override { return new Car(); }
};
Note that the return value of VehicleFactory::create function is Vehicle * where as CarFactory::create is Car *. This is allowed. The CarFactory::create function overrides its parent's virtual function. This feature is called overriding with covariant return types.

What happens when you change the raw pointers to std::shared_ptr? Is it still a valid program?....

As it turns out, it's not. std::shared_ptr (or any simulated covariant type for that matter) can't fool the compiler into believing that the two functions have covariant return types. The compiler rejects the code because as far as it knows, only the pointer types (and references too) have built-in covariance and nothing else.

Lets look a these two factories from the substitutability perspective. The client of VehicleFactory (which has no knowledge of CarFactory) can use VehicleFactory safely even if the create function gets dispatched to CarFactory at run-time. After all, the create function return something that can be treated like a vehicle. No concrete details about Car are necessary for the client to work correctly. That's just classic Object-oriented programming.

Covariance appears to work fine for return types of overridden functions. How about the argument? Is there some sort of variance possible? Does C++ support it? Does it make sense outside C++?

Let's change the create function to accept Iron * as raw material. Obviously, the CarFactory::create must also accept an argument of type Iron *. It is supposed to work and it does. That's old hat.

What if CarFactory is so advanced that it takes any Metal and creates a Car? Consider the following.
struct Vehicle {};
struct Car : Vehicle {};

struct Metal {};
struct Iron : Metal {};

class VehicleFactory {
  public:
    virtual Vehicle * create(Iron *) const { return new Vehicle(); }
    virtual ~VehicleFactory() {}
};

class CarFactory : public VehicleFactory {
public:
    virtual Car * create(Metal *) const override { return new Car(); }
};
The above program is illegal C++. The CarFactory::create does not override anything in its base class and therefore due to the override keyword compiler rejects the code. Without override, the program compiles but you are looking at two completely separate functions marked virtual but really they won't do what you expect.
More interesting question is whether it makes sense to override a function in a way that the argument in the derived function is broader/larger than that of the bases's?...

Welcome to Contravariance...

It totally does make sense and this language feature is called contravariant argument types. From the perspective of the client of VehicleFactory, the client needs to provide some Iron. The CarFactory not only accepts Iron but any Metal to make a Car. So the Client works just fine.

Note the reversed relationship in the argument types. The derived create function accepts the broader type because it must do at least as much as the base's function is able to do. This reverse relationship is the crux of contravariance.

C++ does not have built-in support for contravariant argument types. So that's how it ends for C++? Of course not!

Covariant Return Types and Contravariant Argument Types in std::function

OK, the heading gives it away so lets get right down to an example.
template <class T>
using Sink = std::function<void (T *)>;

Sink<Vehicle> vehicle_sink = [](Vehicle *){ std::cout << "Got some vehicle\n"; };
Sink<Car> car_sink = vehicle_sink; // Works!
car_sink(new Car());

vehicle_sink = car_sink; // Fails to compile
Sink is a function type that accepts any pointer of type T and return nothing. car_sink is a function that accepts only cars and vehicle_sink is a function that accepts any vehicle. Intuitively, it makes sense that if the client needs a car_sink, a vehicle_sink will work just fine because it is more general. Therefore, substitutability works in the reverse direction of parameter types. As a result, Sink is contravariant in its argument type.

std::function is covariant in return type too.
std::function<Car * (Metal *)> car_factory = 
  [](Metal *){ std::cout << "Got some Metal\n"; return new Car(); };

std::function<Vehicle * (Iron *)> vehicle_factory = car_factory;

Vehicle * some_vehicle = vehicle_factory(new Iron()); // Works
Covariance and Contravariance of std::function works with smart pointers too. I.e., std::function taking a shared_ptr of base type is convertible to std::function taking a shared_ptr of derived type.

std::cout << std::is_convertible<std::function<void (std::shared_ptr<Vehicle>)>, 
                                 std::function<void (std::shared_ptr<Car>)>>::value 
          << "\n"; // prints 1.

Sink of a Sink is a Source!

I hope the examples so far have helped build an intuition behind covariance and contravariance. So far it looks like types that appear in argument position should behave contravariantly and types that appear in return position, should behave covariantly. It's a good intuition only until it breaks!
template <class T>
using Source = std::function<void (Sink<T>)>;

Source<Car> source_car = [](Sink<Car> sink_car){ sink_car(new Car()); };

source_car([](Car *){ std::cout << "Got a Car!!\n"; });

Source<Vehicle> source_vehicle = source_car; // covariance!
Type T occurs at argument position in Source. So is Source contravariant in T?...

It's not! It's still covariant in T.

However, Source<T> is contravriant in Sink<T> though.... Afterall, Source is a Sink of a Sink<T>!

OK, still with me?

Let's get this *&%$# straight!

Source<Car> does not really take Car as an argument. It takes Sink<Car> as an argument. The only thing you can really do with it is sink/pass a car into it. Therefore, the lambda passes a new car pointer to sink_car. Again on the next line, calling source_car you have to pass a Sink<Car>. That of course is a lambda that accepts Car pointer as input and simply prints a happy message.

Source<Car> indeed works like a factory of Cars. It does not "return" it. It uses a callback to give you your new car. It's equivalent to returning a new Car. After all, the direction of dataflow is outward. From Callee to the Caller. As the data is flowing outwards, it's covariant.

More formally, type of Source is (T->())->(). A function that takes a callback as an input and returns nothing (i.e., read () as void). As T appears on the left hand side of even number of arrows, it's covariant with respect to the entire type. As simple as that!

Generalizing with Multiple Arguments and Currying

The covariance and contravariance of std::function works seamlessly with multiple argument functions as well as when they are curried.
struct Metal {};
struct Iron : Metal {};
struct Copper : Metal {};

// multiple contravariant position arguments
std::function<Vehicle * (Iron *, Copper *)> vehicle_ic; 
std::function<Car * (Metal *, Metal *)> car_mm = [](Metal *, Metal *) { return new Car(); };
vehicle_ic = car_mm;
vehicle_ic(new Iron(), new Copper());

// Curried versions
std::function<std::function<Vehicle * (Copper *)> (Iron *)> curried_vehicle;
std::function<std::function<Car * (Metal *)> (Metal *)> curried_car;
curried_car = [](Metal *m) { 
  return std::function<Car * (Metal *)>([m](Metal *) { return new Car(); }); 
};  
curried_vehicle = curried_car;
curried_vehicle(new Iron())(new Copper());
The car_mm function can be substituted where vehicle_ic is expected because it accepts wider types and returns narrower types (subtypes). The difference is that these are two argument functions. Each argument type must be at least the same as what's expected by the client or broader.

As every multi-argument function can be represented in curried form, we don't want to throw way our nice co-/contra-variant capabilities of the function-type while currying. Of course, it does not as can be seen from the next example.

The curried_vehicle function accepts a single argument and returns a std::function. curried_car is a subtype of curried_vehicle only if it accepts equal-or-broader type and returns equal-or-narrower type. Clearly, curried_car accepts Metal*, which is broader than Iron*. On the return side, it must return a function-type that is a subtype of the return type of curried_vehicle. Applying the rules of function subtyping again, we see that the returned function type is also a proper subtype. Hence currying is oblivious to co-/contra-variance of argument/return types.

So that's it for now on co-/contra-variance. CIAO until next time!

Live code tested on latest gcc, clang, and vs2015.

For comments see reddit/r/cpp and Hacker News.

CppCon'15 and Silicon Valley Code Camp Presentations

In last couple of months I did a couple of presentations about my recent projects in C++. Session videos, slides, and code for all the presentations are now available online. Both projects have functional programming at their heart. I've found exploring functional programming in modern C++ quite a fun ride. Without further ado, here's the content

CppCon'15: Reactive Stream Processing in Industrial IoT using DDS and RxCpp


Topic: 50 billion devices will be connected to the Internet by 2020. Many of them will belong to national critical infrastructure (smart power grids, smart roads, smart hospitals, smart cities) – forming the Industrial Internet of Things (IIoT). These devices will generate data streams that will need to be correlated, merged, filtered, and analyzed in real-time at the edge. This talk will explore an elegant solution to this problem that is productive, composable, concurrency-friendly, and scales well. We utilize OMG’s Data Distribution Service for Real-Time Systems (DDS) standard for connectivity, and Reactive Extensions (Rx) for functional-style composable asynchronous data processing in modern C++.

Rx is a generalization of futures and can be thought of as the async equivalent of C++ ranges. It helps create asynchronous data processing pipelines by chaining reusable higher-order functions (map, filter, flatmap, zip etc.) that rely on a common abstraction called an Observable (a continuation monad). RxCpp makes wonderful use of functional programming features in modern C++ including generic lambdas, type inference, variadic templates, and more. Rx is one of the best libraries that truly highlights the power of functional design principles applied in a (primarily) object-oriented programming languages.

DDS and Rx work great together because they are both reactive, use the publish-subscribe paradigm, and facilitate loose coupling between components. This presentation will discuss Rx4DDS, which is a research library that integrates Rx with RTI Connext DDS. Rx4DDS enables a clean, distributed, asynchronous dataflow architecture for stream processing and is available in C#, C++, and JavaScript.

Slides



More reading

  • Data-Centric Stream Processing in the Fog is an RTI blog post with detailed description of one of the demonstrations and code I showed at CppCon'15. If you know what I mean by "The finalization actions are baked into each data pipeline at the time of creation" you can skip right ahead.

  • Rx4DDS home page includes all the demonstrations and code I showed at CppCon. The description is somewhat sparse and assumes that you have seen the earlier resources listed here.


Silicon Valley Code Camp: Composable Generators and Property-based Testing in C++14  


Topic: C++14 has an enviable collection of functional programming features such as generic lambdas, type inference, variadic templates, function types with co-/contra-variance and so on. With mature compiler support, designing and implementing performant functional-style libraries has become very pleasant in modern C++. Tools and techniques (e.g., property-based testing) enjoyed by the programmers in only elite functional languages (Haskell, Scala) now appear to be within C++'s reach.

This presentation will discuss two classic techniques from the functional domain -- composable data generators and property-based testing -- implemented in C++14 for testing a generic serialization and deserialization library (RefleX). We will look at techniques of constructing complex generators using a random number generator and a tolerable dose of monoids, functors, and of course, monads. We won't stop there though! We will look at automatic type generators using C++ TMP. Equipped with data and type generators, we'll take property-based testing to a whole new level where lazy programmers don't have to do anything to test their programs beyond just compilation and running the test over and over.

Code on github: generators

Slides 




Bonus Content: Channel9 Interview at CppCon'15

Here's my really short interview recorded at CppCon'15 by Channel9. Yes, it's about functional programming! Skip ahead to 45m36s into the video to checkout my segment. Alternatively, click here.