fighting with move sematics and std::tuple
Hi,
i am working on an exercise for move sematics and want to create a
'FunctionTracker', which logs enter/exit message for functions and
their input/output variables.
It works. But it uses some copying, which i want to shake off (if
possible).
Here's the FunctionTracker.h source file (i use gcc 4.7.0, ubuntu
linux 10.04.4, 32 bit):
-------------------------------------------------------------
/*
* FunctionTracker.h
*/
#ifndef FUNCTIONTRACKER_H_
#define FUNCTIONTRACKER_H_
#include <iostream>
#include <iomanip> // std::setw
#include <utility> // std::forward<>
#include <tuple> // std::tuple<>
#include <type_traits> // std::enable_if<>
/**
* classify input vs. output parameter
* to log for entering vs. exiting a procedure/function
*/
typedef enum
{
Input_c = 1,
Output_c = 2
} ParamType_t;
/**
* parameter type-independent base class
*/
struct _ArgCommon
{
char const * const _name;
ParamType_t const _type;
_ArgCommon(
char const * const name,
ParamType_t const type)
: _name(name),
_type(type)
{ };
#if 0
// TODO: want to get rid of the copy c'tor
_ArgCommon(_ArgCommon const &) = delete;
#else
_ArgCommon(
_ArgCommon const &rhs)
: _name(rhs._name),
_type(rhs._type)
{
std::cerr << "_ArgCommon copy c'tor" << std::endl;
};
#endif
_ArgCommon & operator=(_ArgCommon const &) = delete;
};
/**
* host class for parameter
* referencing to the variable
*/
template <typename T>
struct _Arg : public _ArgCommon
{
const T & _value;
_Arg(
char const * const name,
ParamType_t const type,
T const & value)
: _ArgCommon(name, type),
_value(value)
{ };
#if 0
// TODO: want to get rid of the copy c'tor
_Arg(_Arg const & rhs) = delete;
#else
_Arg(
_Arg const & rhs)
: _ArgCommon(rhs),
_value(rhs._value)
{ };
#endif
_Arg & operator=(_Arg const &) = delete;
};
/**
* ostream helper for _Arg<T>
*/
template <typename T>
std::ostream &operator<<(
std::ostream &out,
const _Arg<T> &arg)
{
out << arg._name << " => '" << arg._value << "'";
return out;
}
/**
* input parameter variant
*/
template <typename T>
struct InArg : public _Arg<T>
{
InArg(
char const * const name,
T const & value)
: _Arg<T>(name, Input_c, value)
{ };
#if 0
// TODO: want to get rid of the copy c'tor
InArg(InArg const & rhs) = delete;
#else
InArg(
InArg const & rhs)
: _Arg<T>(rhs)
{ };
#endif
InArg & operator=(InArg const &) = delete;
};
/**
* output parameter variant
*/
template <typename T>
struct OutArg : public _Arg<T>
{
OutArg(
char const * const name,
T const & value)
: _Arg<T>(name, Output_c, value)
{ };
#if 0
// TODO: want to get rid of the copy c'tor
OutArg(OutArg const & rhs) = delete;
#else
OutArg(
OutArg const & rhs)
: _Arg<T>(rhs)
{ };
#endif
OutArg & operator=(OutArg const &) = delete;
};
template <typename T>
const InArg<T> MakeInArg(
char const * const name,
T & value)
{
std::cerr << "MakeInArg: invoked" << std::endl;
return InArg<T>(name, value);
};
template <typename T>
const OutArg<T> MakeOutArg(
char const * const name,
T & value)
{
return OutArg<T>(name, value);
};
// variadic template type list transformation
template <typename ...Types>
struct _ArgTuple
{
typedef typename std::tuple<const _Arg<Types>...> Type;
};
/**
* recursive tuple ostream helper template
*/
/**
* recursion
*/
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
PrintTuple(const std::tuple<Tp...>& t)
{
std::cerr << std::get<I>(t) << ", ";
PrintTuple<I + 1, Tp...>(t);
}
/**
* recursion termination
*/
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
PrintTuple(const std::tuple<Tp...>& t)
{ }
/**
* shared recursion level
* (does not support threads!)
*/
struct _FunctionTrackerShared {
static int _level;
};
/**
* FunctionTracker implementation
* but not used directly
* 'FunctionTracker' is used instead
*/
template <typename PARAMS>
class _FunctionTrackerImpl
{
private:
char const * const _funcName;
const PARAMS _params;
protected:
_FunctionTrackerImpl(
char const* const funcName,
PARAMS && params)
: _funcName(funcName?funcName:"<unknown>"),
_params(std::forward<PARAMS>(params))
{
if (_FunctionTrackerShared::_level > 0) {
std::cerr << std::setw(_FunctionTrackerShared::_level) << ' ';
}
std::cerr << _funcName << ": enter with ";
PrintInput(_params);
std::cerr << std::endl;
++_FunctionTrackerShared::_level;
}
#if 0
_FunctionTrackerImpl(const _FunctionTrackerImpl &) = delete;
#else
_FunctionTrackerImpl(const _FunctionTrackerImpl & rhs)
: _funcName(rhs._funcName),
_params(rhs._params)
{
std::cerr << "!!! _FunctionTrackerImpl copy c'tor !!!" <<
std::endl;
}
#endif
~_FunctionTrackerImpl()
{
--_FunctionTrackerShared::_level;
if (_FunctionTrackerShared::_level > 0) {
std::cerr << std::setw(_FunctionTrackerShared::_level) << ' ';
}
std::cerr << _funcName << ": exit with ";
PrintOutput(_params);
std::cerr << std::endl;
}
void PrintInput(
const PARAMS & params)
{
std::cerr << "input: ";
PrintTuple(Input_c, params);
}
void PrintOutput(
const PARAMS & params)
{
std::cerr << "output: ";
PrintTuple(Output_c, params);
}
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
PrintTuple(ParamType_t const & paramType, const std::tuple<Tp...>&
t)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
PrintTuple(ParamType_t const & paramType, const std::tuple<Tp...>&
t)
{
const _ArgCommon& arg = std::get<I>(t);
if (paramType == arg._type) {
std::cerr << std::get<I>(t) << ", ";
}
PrintTuple<I + 1, Tp...>(paramType, t);
}
};
/**
* FunctionTracker factory supporting variable arguments
*/
template <typename... Types>
class FunctionTracker : public _FunctionTrackerImpl< typename
_ArgTuple<Types...>::Type >
{
private:
typedef _FunctionTrackerImpl< typename _ArgTuple<Types...>::Type >
BaseType;
typedef typename _ArgTuple<Types...>::Type PARAMS;
FunctionTracker(
char const * const funcName,
PARAMS && params)
: _FunctionTrackerImpl<PARAMS>(funcName,
std::forward<PARAMS>(params))
{ };
public:
FunctionTracker(const FunctionTracker & rhs)
: BaseType(rhs)
{ };
template <typename... Args>
static FunctionTracker make(
char const * const funcName,
Args... args)
{
std::cerr << "FunctionTracker::make(...): create tuple" <<
std::endl;
auto forTest = std::make_tuple(std::forward<Args>(args)...);
std::cerr << "FunctionTracker::make(...): create and return
FunctionTracker" << std::endl;
return FunctionTracker<Types...>(funcName,
std::make_tuple(std::move(args)...));
};
};
#endif /*FUNCTIONTRACKER_H_*/
-------------------------------------------------------------
And here a little test main.cpp:
-------------------------------------------------------------
/*
* main.cpp
*
* Created on: Oct 30, 2011
* Author: frank
*/
#include <utility> // std::move<>
#include "FunctionTracker.h"
int
main(
int argc,
char ** argv)
{
int x = 5;
auto funcTracker = FunctionTracker<
std::remove_reference<decltype(x)>::type
::make(
__FUNCTION__,
MakeInArg("x", x));
return 0;
}
-------------------------------------------------------------
And this is the output:
-------------------------------------------------------------
frank@frank-desktop:~/workspace/FunctionTracker/Debug$ ./
FunctionTracker
MakeInArg: invoked
FunctionTracker::make(...): create tuple
_ArgCommon copy c'tor
FunctionTracker::make(...): create and return FunctionTracker
_ArgCommon copy c'tor
_ArgCommon copy c'tor
_ArgCommon copy c'tor
main: enter with input: x => '5',
main: exit with output:
-------------------------------------------------------------
See, there is a copy c'tor invoked #1 time for just creating the
tuple.
(Added in the FunctionTracker::make(...) just for test.
And in know, that i shouldn't apply std::forward and std::move both
to the same data - just for test.
Btw, i tries both for the 'FunctionTracker': std::move and
std::forward - no difference.)
And there is even copied #3 times for creating the real
'FunctionTracker' object.
Can you pls. help?
- Many thanks!
best regards,
Frank
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]