This C++ code sample demonstrates using tuples and polymorphic functions to allow threads to modify shared variables through a transaction thread, without the threads needing to know indexes into the tuple. It defines a UniqueMapper class that wraps variables in unique types, and a TupleMapper class that stores the UniqueMappers in a tuple and allows getting/setting their underlying variables via type deduction. Main() tests it by creating UniqueMappers for shared values, putting them in a TupleMapper, and having "threads" modify values by passing UniqueMappers without knowing tuple indexes.
1. 1C:my_docsvirtual_machinesvmware_playershared_foldergeneralcpp11_sample_linux.cpp
/*
* tuple_mapper.hpp
*
* Created on: 19 Feb 2014
* Author: Dr Russell John Childs.
*/
//=======================================================================================================
// COPYRIGHT NOTICE
// This code sample and ideas embodied remain the property of Dr Russell John Childs, PhD, and have been
// distributed as a representative example of my use of C++11 features.
//==========================================================================================================
============
#ifndef TUPLE_MAPPER_HPP_
#define TUPLE_MAPPER_HPP_
//=========================================================================
// Use Case:
// Several threads operate on a set of variables, one variable to each thread.
//
// A transaction thread pulls these variables into a transaction (e.g. relativistic transfer of four-
momentum).
// The transaction thread operates on temporary copies so the transaction can be rolled back.
//
// During the transaction, a thread may want to change a variable, but does not know the tuple
// index for an std::get<index>(transaction_tuple)=new_value
//
// Also, changing a variable inside the tuple will not be visible to the transaction thread so that it can
revert transaction.
//
// Solution:
// Threads use polymorphic function that forwards requests for a change to a variable
// to the transaction thread.
//
// Since the threads neither know the index for their variable in the tuple, nor could they store one since
// it is a runtime value, not a compile time value, they instead make their variable a unique user type:
// e.g. UniqueMapper<float, 10>(my_float, 20.6), the unique id here is 10.
//
// Unfortunately, std::get<MyType>(transaction_tuple)=new_val is not allowed (const return type), nor does
std::get<MyType>(transaction_tuple);
// even compile under g++ 4.8, even though it should, so
// have to implement our own version using multiple inheritance (see class TupleMapper)
//
// Compiling this code sample (Linux Mint - g++ 4.8)
// Uncomment main()
// File: my_file.cpp
// #include "[my_directory]/tuple_mapper.hpp"
//
// g++ -O0 -g3 -Wall -O2 -g -Wall -c -fmessage-length=0 -std=c++11
// -I/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/
lib/x86_64-linux-gnu/4.8/:
// /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/:/usr/lib/gcc/
x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/lib/../lib/:
// /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/.
./../../x86_64-linux-gnu/:
// /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/:/lib/x86_64-linux-gnu/4.8/:/lib/x86_64-linux-gnu/
:/lib/../lib/:/usr/lib/x86_64-linux-gnu/4.8/:
// /usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-
linux-gnu/lib/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:
// /lib/:/usr/lib/
// my_file.cpp
//=========================================================================
#include <typeinfo>
#include <cxxabi.h>
#include <iomanip>
#include <functional>
#include <iostream>
#include <atomic>
#include <string>
#include <vector>
#include <tuple>
2. 2C:my_docsvirtual_machinesvmware_playershared_foldergeneralcpp11_sample_linux.cpp
//=======================================================================================
// Class: struct UniqueMapper (lightweight std::pair)
// Template parameters:
// Type: the underlying type, e.g. float, int, MyClass
// Unique: A unique unsigned*
// Usage:
// float float_1;
// float float_2;
// //unique float (copy by value)
// auto unique_1 = UniqueMapper<decltype(float_1),&S>(float_1);
// //unique float (copy by ref)
// auto unique_2 = UniqueMapper<decltype(float_2),&S>(std::ref(float_1).get());
//=======================================================================================
//Helper macro - usage:
// auto unique_1 = __GETUNIQUE__(float_1);
// auto unique_2 = __GETUNIQUE__(std::ref(float_1).get());
template<typename T> struct dummy{ dummy(){} };
#define __GETUNIQUE__(variable) [&]{static unsigned S; return UniqueMapper<decltype(variable),&S>(variable);
}()
template<typename Type, unsigned* Unique>
struct UniqueMapper
{
//----------------------------------------------------
// Ctor:
// Arguments:
// Type& variable: the initial value of the variable
//----------------------------------------------------
UniqueMapper( const Type& variable ) :
m_variable( variable )
{
}
//----------------------------------------------------
// Dtor
//----------------------------------------------------
~UniqueMapper( void )
{
}
//----------------------------------------------------
// Method: get
// Arguments:
// None.
// Return value:
// Underlying variable by ref.
//----------------------------------------------------
Type& get( void )
{
return m_variable;
}
private:
template<typename... T> friend class TupleMapper;
Type m_variable;
};
//=======================================================================================
// Class: struct TupleMapper
// Template parameters:
// Type: the underlying types, e.g. float, int, MyClass
// Usage: Only serves as primary template.
//=======================================================================================
template<typename... Type>
struct TupleMapper
{
};
//=======================================================================================
// Class: struct TupleMapper
// Template parameters:
3. 3C:my_docsvirtual_machinesvmware_playershared_foldergeneralcpp11_sample_linux.cpp
// Type: the underlying types, e.g. float, int, MyClass
// UniqueID: the unique unsigned integers distinguishing underlying types, e.g. 1,2,3,4
// Usage:
// UniqueMapper<float, 1> float_1(10);
// UniqueMapper<float, 2> float_2(20);
// TupleMapper<UniqueMapper<float, 2>, UniqueMapper<float, 1>> tup(float_2, float_1);
// tup.get(float_1)=30; // type deduction
// tup.get<decltype(float_2)>()=float_2.get(); // explicit type declaration
//=======================================================================================
template<typename...Type, unsigned*... Unique>
struct TupleMapper<UniqueMapper<Type,Unique>...> : public UniqueMapper<Type, Unique>...
{
//----------------------------------------------------
// Ctor:
// Arguments:
// UniqueMapper<Type,UniqueID>&... maps: the unique variables
//----------------------------------------------------
TupleMapper( UniqueMapper<Type,Unique>... maps) :
UniqueMapper<Type, Unique>(maps)...
{
}
//----------------------------------------------------
// Dtor
//----------------------------------------------------
~TupleMapper( void )
{
}
//----------------------------------------------------
// Method: get
// Template parameters:
// T: the unique type, e.g. UniqueID<float, 3>
// Arguments:
// None
// Returns:
// Reference to underlying variable
// Usge: tuple_mapper.get(UniqueID<float, 1>) = 30;
//----------------------------------------------------
template<typename T > auto get( void )-> decltype(T::m_variable)&
{
return T::m_variable;
}
//----------------------------------------------------
// Method: get
// Template parameters:
// T: the underlying type, e.g. float, automatically obtained through type deduction
// U: the unique integer id, e.g. 3, automatically obtained through type deduction
// Arguments:
// Unique variable
// Returns:
// Reference to underlying variable
// Usage: tup_map.get<decltype(float_2)>() = 40;
//----------------------------------------------------
template<typename T, unsigned* U> auto get( const UniqueMapper<T,U>&) -> decltype(this->get
<UniqueMapper<T,U>>())
{
return get<UniqueMapper<T,U>>();
}
};
//=======================================
// Helper classes for extracting the type of a variable
//=======================================
//=======================================
// const char* demangle( const char *str )
// demangles C++ decorated names
//==========================================
const char* demangle( const char *str )
{
int status;
4. 4C:my_docsvirtual_machinesvmware_playershared_foldergeneralcpp11_sample_linux.cpp
return abi::__cxa_demangle(str,0,0,&status);
}
//=======================================
// template<typename T> struct IsType
// When typeid is used on Type& it prints out "Type" rather than "Type&"
// This class forces typeid to print out "IsType<Type&>" enabling "Type&" to be extracted
//==========================================
template<typename T> struct IsType
{
};
//=======================================
// Following macros extract "Type&" substr from "IsType<Type&>" string
// Usage:
// For objects:
// float& my_float_ref = some_float_var;
// typeid(decltype(my_float_ref)).name(); - gives "float"
// GET_TYPE_NAME(my_float_ref); - gives "float&"
// For types:
// template< typename T> struct MyStruct
// {
// MyStruct( void ){ std::cout << GET_TEMPLATE_NAME(T) << std::emdl; } // MyStruct<int&> gives "int
&".
// } ;
//==========================================
#define GET_TYPE_NAME_STR(arg) std::string(demangle(typeid(IsType<decltype(arg)>).name()))
#define GET_TYPE_NAME(arg) GET_TYPE_NAME_STR(arg).substr(7, GET_TYPE_NAME_STR(arg).length()-8 )
#define GET_TYPE_NAME_STR_1(arg) std::string(demangle(typeid(IsType<arg>).name()))
#define GET_TEMPLATE_NAME(arg) GET_TYPE_NAME_STR_1(arg).substr(7, GET_TYPE_NAME_STR_1(arg).length()-8 )
template<typename First, typename... Rest> struct MetaProgramming
{
MetaProgramming( const int i = 0 )
{
float my_float;
float& my_float_ref = my_float;
//typeid doesn't display references
std::cout << "typeid: " << "= " << demangle(typeid(decltype(my_float_ref)).name()) << std::endl;
//display reference type of template parameters
std::cout << "Type " << i << "=" << GET_TEMPLATE_NAME(First) << std::endl;
MetaProgramming<Rest...> tmp(i+1);
}
};
template<typename First> struct MetaProgramming<First>
{
MetaProgramming( const int i = 0 )
{
//Display type of an object, IsType<First>()
std::cout << "Type " << i << "=" << GET_TYPE_NAME(IsType<First>()) << std::endl;
}
};
//=======================================
// Following runs the above classes for illustration purposes.
//=======================================
class TestTupleMapperAndMetaProgramming
{
public:
TestTupleMapperAndMetaProgramming(void)
{
try
{
thread();
//throw std::runtime_error("test");
}
catch(std::exception& ex)
{
std::cout << "Execption" << std::endl
<< "File: " __FILE__ << ", Line: " << __LINE__
5. 5C:my_docsvirtual_machinesvmware_playershared_foldergeneralcpp11_sample_linux.cpp
<< ", what()=" << ex.what() << std::endl;
}
}
~TestTupleMapperAndMetaProgramming( void )
{
}
template<typename T>
void compiler_bug(void)
{
//Without const qualifier T& become universal reference if T is a reference, e.g. T=float& --> float
&& (a universal ref)
//But, with const qualifier float&& should be an r-value, not a universal.
float my_float;
const T& tmp(my_float);
T const& tmp1(my_float);
std::cout << "compiler bug " << GET_TYPE_NAME(tmp) << std::endl;
}
void thread( void )
{
compiler_bug<float&&>();
compiler_bug<float&>();
compiler_bug<float>();
//Test __GETUNIQUE__ macro
int ii;
auto type = __GETUNIQUE__(ii);
std::cout << GET_TYPE_NAME( type ) << std::endl;
std::cout << std::boolalpha << (typeid(__GETUNIQUE__(ii)) != typeid(__GETUNIQUE__(ii))) << std::endl
;
//thread_f uses a float, but must make it a unique type, pass by ref
float f=-1;
auto u_f=__GETUNIQUE__(std::ref(f).get());
std::cout << GET_TYPE_NAME( u_f ) << std::endl;
//thread_i uses an int, but must make it a unique type, PASS by REF
int i=-1;
auto u_i=__GETUNIQUE__(std::ref(i).get());
std::cout << GET_TYPE_NAME( u_i ) << std::endl;
//thread_s uses a string, but must make it a unique type, PASS by REF
std::string s="-1";
auto u_s=__GETUNIQUE__(std::ref(s).get());
std::cout << GET_TYPE_NAME( u_f ) << std::endl;
//thread_j uses an int, but must make it a unique type, PASS by VALUE
int j=-1;
auto u_j=__GETUNIQUE__(j);
std::cout << GET_TYPE_NAME( u_j ) << std::endl;
//thread_tuple copies all above variables into a temporary tuple so that it can perform a
transaction without a commit
//If any of the above treads changes a variable, the transaction must be undone.
//The above threads change their variables through a polymorphic function that forwards request to
transaction thread.
TupleMapper<decltype(u_f), decltype(u_i), decltype(u_s), decltype(u_j)> tup(u_f, u_i, u_s, u_j);
std::cout << "initial values:" << std::endl
<< " tup.get<decltype(u_f)>()=" << tup.get<decltype(u_f)>()
<< ", f=" << f << std::endl
<< " tup.get<decltype(u_i)>()=" << tup.get<decltype(u_i)>()
<<", i=" << i << std::endl
<< " tup.get<decltype(u_s)>()=" << tup.get<decltype(u_s)>()
<< ", s=" << s << std::endl
<< " tup.get<decltype(u_j)>()=" << tup.get<decltype(u_j)>()
<< ", j=" << j << std::endl;
//thread i requests thread_tuple to change its value to 10, but does not know index for get<index>
(tuple)
//send u_i instead
6. 6C:my_docsvirtual_machinesvmware_playershared_foldergeneralcpp11_sample_linux.cpp
u_i.get()=10;
//tuple_thread can now index tuple by type deduction
tup.get(u_i)=u_i.get();
//thread s requests thread_tuple to set its value to "s=10", but does not know index for get<index>
(tuple)
//send u_s instead
u_s.get()="s=10";
//tuple_thread can also index tuple by decltype
tup.get<decltype(u_s)>()=u_s.get(); //thread s sends UniqueMapper<std::string,4>
//thread f requests thread_tuple to set its value to 10.10, but does not know index for get<index>
(tuple)
tup.get(u_f)=10.10;//thread f sends UniqueMapper<float,3>
//thread j requests thread_tuple to set its value to 20, but does not know index for get<index>
(tuple)
tup.get(u_j)=20; //thread j sends UniqueMapper<int,2>
std::cout << "final values:" << std::endl
<< " tup.get<decltype(u_f)>()=" << tup.get<decltype(u_f)>()
<< ", f=" << f << std::endl
<< " tup.get<decltype(u_i)>()=" << tup.get<decltype(u_i)>()
<<", i=" << i << std::endl
<< " tup.get<decltype(u_s)>()=" << tup.get<decltype(u_s)>()
<< ", s=" << s << std::endl
<< " tup.get<decltype(u_j)>()=" << tup.get<decltype(u_j)>()
<< ", j=" << j << std::endl;
//Test example of metaprogramming
MetaProgramming<float&, int, std::string, std::vector<int>& > tm;
}
};
// executing illustration:
int main( void )
{
TestTupleMapperAndMetaProgramming();
}
#endif /* TUPLE_MAPPER_HPP_ */