简体   繁体   中英

Function for type converting or forwarding (depending on input type)

I have a piece of code which does something like this:

void some_func(SomeType st) {
    some_stuf...
    dosomething( st.myStruct() );
    some_more_stuff...
}

This SomeType::myStruct is of type MyStruct . This SomeType goes through some serialization, and on the other side of communication channel I get deserialized SomeType which is called AlmostSomeType which has the same fields, except that myStruct is of type std::string (it couldn't be deserialized to MyStruct ).

MyStruct can be created from std::string .

Now, to not create additional versions of some_func (DRY!) I'm going to do this:

template< typename Type >
void some_func( Type type ) {
    some_stuf...
    dosomething( getMyStruct( type.myStruct() ) ); 
    // or even something like: 
    const MyStruct& myStruct = getMyStruct( type.myStruct() );
    more calls to myStruct...
    some_more_stuff...
}

where getMyStruct looks like this:

template< typename T >
T getMyStruct( T&& aT )
{ return aT; }

MyStruct getMyStruct( std::string myStructString )
{ return MyStruct( myStructString ); }

Now here's what I know/think :

  1. I know it doesn't look nice, but real-life project's don't look nice. I cannot modify AlmostSomeType and SomeType .
  2. I do not want to create any additional overhead, that's why I used universal reference which will handle any type of MyStruct (reference, const reference, etc), and std::string version for strings.
  3. Flaws? If someone will put anything else than MyStruct or std::string into getMyStruct it might pass if parameter-constructors of MyStruct aren't explicit .
  4. I've done simple tests with MyStruct consisting of all the needed ctors and assignment operators, compiled code with -O2 and got the results I expected, ie no copy-ctors called.

I'd like to hear your opinions if this is the right way, ie if this function template approach is OK or are there some caveats I should be aware of. I know that I'm counting a bit on compilers optimizations, but hey! You cannot depend only on language semantics.

Environment: gcc (4.7.2), -std=c++11, -O2

EDIT

Some example code I was using:

struct MyStruct
{
    MyStruct() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    explicit MyStruct( std::string a ) : dummy_(a.size()) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    MyStruct( const MyStruct& aOther ) : dummy_(aOther.dummy_) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    MyStruct( MyStruct&& aOther ) : dummy_(std::move(aOther.dummy_)){ std::cout << __PRETTY_FUNCTION__ << "\n"; } 
    ~MyStruct() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    MyStruct& operator=( const MyStruct& aOther ) { std::cout << __PRETTY_FUNCTION__ << "\n"; return *this; }
    MyStruct& operator=( MyStruct&& aOther ) { std::cout << __PRETTY_FUNCTION__ << "\n"; return *this; }

    double dummy_;
    char cdummy_[100];
};

struct MyAggregate
{
    MyStruct myStruct_;
    std::string myString_;

    const MyStruct& myStruct() { return myStruct_; }
    const std::string& myString() { return myString_; }

    MyAggregate() :myString_("four") { myStruct_.dummy_ = 5.5; }
};


int main()
{
    MyAggregate myAggregate;
    std::cout << "---\n";
    const MyStruct& ms1 = getMyStruct( myAggregate.myStruct_ );
    std::cout << "---\n";
    const MyStruct& ms2 = getMyStruct( myAggregate.myString_ );
    std::cout << "---\n";
    const MyStruct& ms3 = getMyStruct( myAggregate.myStruct() );
    std::cout << "---\n";
    const MyStruct& ms4 = getMyStruct( myAggregate.myString() );
    std::cout << "---\n";
    return 0;
}

Output I got:

MyStruct::MyStruct()
---
T getMyStruct(T&&) [with T = MyStruct&]
---
MyStruct getMyStruct(std::string)
MyStruct::MyStruct(std::string)
---
T getMyStruct(T&&) [with T = const MyStruct&]
---
MyStruct getMyStruct(std::string)
MyStruct::MyStruct(std::string)
---
MyStruct::~MyStruct()
MyStruct::~MyStruct()
MyStruct::~MyStruct()

There is excessive copy of string (when you are passing it by value) in your solution. Why not extract special case of string and then process it separately?

template<typename T, typename = 
   typename enable_if<!is_same<decay_t<T>,string>::value>::type >
decltype(auto) getMyStruct( T&& aT )
{ return aT; }

template<typename T, typename = 
    typename enable_if<is_same<decay_t<T>,string>::value>::type >
MyStruct getMyStruct (T &&aT)
{ return MyStruct (aT);}

I dropped some std:: prefixes here above to make code look neater.

On GCC 5.1 output is:

MyStruct::MyStruct()
---
decltype(auto) getMyStruct(T&&) [with T = MyStruct&; <template-parameter-1-2> = void]
---
MyStruct getMyStruct(T&&) [with T = std::__cxx11::basic_string<char>&; <template-parameter-1-2> = void]
MyStruct::MyStruct(std::__cxx11::string)
---
decltype(auto) getMyStruct(T&&) [with T = const MyStruct&; <template-parameter-1-2> = void]
---
MyStruct getMyStruct(T&&) [with T = const std::__cxx11::basic_string<char>&; <template-parameter-1-2> = void]
MyStruct::MyStruct(std::__cxx11::string)
---
MyStruct::~MyStruct()
MyStruct::~MyStruct()
MyStruct::~MyStruct()

I compiled with ~/x86-toolchain-5.1/bin/g++ mystr.cpp --std=c++14

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM