Re: fighting with move sematics and std::tuple
Hi Daniel,
thanks for all the hints!
The most relevant was this:
template<typename ...Types>
struct _ArgTuple
{
typedef typename std::tuple<const _Arg<Types>...> Type;
};
There is a lot of code, but above member type definition is at least
one source of the trouble, see below.
[...]
It is easy to understand that *any*
std::tuple<const X> cannot be a move-only type for some object type X,
simply because the move-constructor would be ill-formed when there is
a const data member.
So here's the updated version:
FunctionTracker.h:
----------------------------------------------------------
/*
* FunctionTracker.h
*
* Created on: Oct 30, 2011
* Author: frank
*/
#ifndef FUNCTIONTRACKER_H_
#define FUNCTIONTRACKER_H_
#include <string>
#include <iostream>
#include <iomanip> // std::setw
#include <utility> // std::forward<>
#include <tuple> // std::tuple<>
#include <type_traits> // std::enable_if<>
#include <stdexcept> // std::runtime_error
namespace FT // FunctionTracker
{
/**
* parameter type-independent base class
*/
struct ArgCommon
{
/**
* classify input vs. output parameter
* to log for entering vs. exiting a procedure/function
*/
typedef enum
{
Input_c = 1,
Output_c = 2
} ParamType_t;
char const * const name;
ParamType_t const type;
ArgCommon(
char const * const name,
ParamType_t const type)
: name(name),
type(type)
{ };
ArgCommon(ArgCommon&& rhs)
: name(rhs.name),
type(rhs.type)
{ };
ArgCommon(
ArgCommon const &rhs)
: name(rhs.name),
type(rhs.type)
{
throw std::runtime_error("ArgCommon: invalid invocation of copy
c'tor");
};
ArgCommon & operator=(ArgCommon const &) = delete;
};
/**
* host class for parameter_name << ": enter" <<
* referencing to the variable
*/
template <typename T>
struct Arg : public ArgCommon
{
const T & value;
Arg(
char const * const name,
ArgCommon::ParamType_t const type,
T const & value)
: ArgCommon(name, type),
value(value)
{ };
Arg(Arg&& rhs)
: ArgCommon(std::move(rhs)),
value(rhs.value)
{ };
Arg(
Arg const & rhs)
: ArgCommon(rhs), // will throw
value(rhs.value)
{ };
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;
}
template <typename T>
Arg<T> MakeInArg(
char const * const name,
T & value)
{
return Arg<T>(name, ArgCommon::Input_c, value);
};
template <typename T>
Arg<T> MakeOutArg(
char const * const name,
T & value)
{
return Arg<T>(name, ArgCommon::Output_c, value);
};
// variadic template type list transformation
template <typename ...Types>
struct ArgTuple
{
typedef typename std::tuple<Arg<Types>...> Type;
};
/**
* 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::move(params))
{
if (FunctionTrackerShared::level > 0) {
std::cerr << std::setw(FunctionTrackerShared::level) << ' ';
}
std::cerr << funcName << ": enter with input: ";
PrintTuple(ArgCommon::Input_c, params);
std::cerr << std::endl;
++FunctionTrackerShared::level;
}
FunctionTrackerImpl(const FunctionTrackerImpl & rhs)
: funcName(rhs.funcName),
params(rhs.params)
{
throw std::runtime_error("FunctionTrackerImpl: invalid invocation
of copy c'tor");
}
FunctionTrackerImpl & operator=(const FunctionTrackerImpl &rhs) =
delete;
~FunctionTrackerImpl()
{
--FunctionTrackerShared::level;
if (FunctionTrackerShared::level > 0) {
std::cerr << std::setw(FunctionTrackerShared::level) << ' ';
}
std::cerr << funcName << ": exit with output: ";
PrintTuple(ArgCommon::Output_c, params);
std::cerr << std::endl;
}
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
PrintTuple(ArgCommon::ParamType_t const & paramType, const
std::tuple<Tp...>& t)
{ }
// TODO: turn dynamic checks into compile time evaluation
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
PrintTuple(ArgCommon::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);
if (I+1!=sizeof...(Tp)) std::cerr << ", ";
}
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)
: BaseType(funcName, std::move(params))
{ };
public:
// cannot delete copy c'tor
// because FunctionTracker::Make formally requires it
// though doesn't actually use if (RVO)
FunctionTracker(const FunctionTracker & rhs)
: BaseType(rhs) // will throw
{ };
FunctionTracker & operator=(const FunctionTracker &rhs) = delete;
template <typename... Args>
static FunctionTracker make(
char const * const funcName,
Args... args)
{
return FunctionTracker<Types...>(funcName,
std::make_tuple(std::move(args)...));
};
};
} // namespace FT
#endif /*FUNCTIONTRACKER_H_*/
----------------------------------------------------------
FunctionTracker.cpp:
----------------------------------------------------------
/*
* FunctionTracker.cpp
*
* Created on: Oct 30, 2011
* Author: frank
*/
#include "FunctionTracker.h"
int FT::FunctionTrackerShared::level = 0;
----------------------------------------------------------
main.cpp:
----------------------------------------------------------
/*
* main.cpp
*
* Created on: Oct 30, 2011
* Author: frank
*/
#include <utility>
#include "FunctionTracker.h"
int
main(
int argc,
char ** argv)
{
int x = 5;
std::cerr << "### main: create tuple" << std::endl;
std::tuple<FT::Arg<int> > forTest =
std::make_tuple(FT::MakeInArg("x", x));
std::cerr << "### main: create FunctionTracker" << std::endl;
auto funcTracker = FT::FunctionTracker<
std::remove_reference<decltype(x)>::type
::make(
__FUNCTION__,
FT::MakeInArg("x", x));
return 0;
}
----------------------------------------------------------
output:
----------------------------------------------------------
frank@frank-desktop:~/workspace/FunctionTracker/Debug$ ./
FunctionTracker
### main: create tuple
### main: create FunctionTracker
main: enter with input: x => '5'
main: exit with output:
----------------------------------------------------------
The reason for need of copy constructor seems to be the
FunctionTracker::make(...).
it uses:
return FunctionTracker<Types...>(funcName,
std::make_tuple(std::move(args)...));
I guess this "formally" requires the copy c'tor(?)
(see my comment:
// cannot delete copy c'tor
// because FunctionTracker::Make formally requires it
// though doesn't actually use if (RVO)
)
That's why i cannot use "= delete" for those.
However, as the copy c'tor (now) are not used actually, i implemented
them, but raise a runtime exception in case they WOULD be used.
I want the Function::Tracker::make(...) to serve for type-
transformation.
Is there a better way to resolve this conflict?
regards,
Frank
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]