1

I'm new to templates, so I decided to write out unit tests for some concurrent code I am writing, but I can't seem to get them to compile. The specific error is:

error C2664: 'std::thread::thread(const std::thread &)' : cannot convert argument 1 from       'void (__cdecl *)(lock &)' to 'void (__cdecl &)(Utility_UnitTests::emptyLock &)'
1>          None of the functions with this name in scope match the target type
1>          w:\code dumpster\utility_unittests\utspinlock.cpp(88) : see reference to function template instantiation 'void Utility_UnitTests::UTSpinLock::lockContension<Utility_UnitTests::emptyLock>(lock &)' being compiled
1>          with
1>          [
1>              lock=Utility_UnitTests::emptyLock
1>          ]

The issue is pretty clear from the compiler, I am not passing the correct type, but I have no clue how to fix it! Thanks in advance!

EDIT: I forgot to mention, I am using Visual Studio 2013

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Utility_UnitTests
{
typedef utils::threading::SpinLock<utils::threading::backoff::empty> emptyLock;
typedef utils::threading::SpinLock<utils::threading::backoff::yield> yieldingLock;
typedef utils::threading::SpinLock<utils::threading::backoff::pause> pausingLock;

TEST_CLASS(UTSpinLock)
{
public:
    template<typename lock>
    void lockAndSleepT(lock &l)
    {
        l.lock();
        std::this_thread::sleep_for(std::chrono::nanoseconds(10));
        l.unlock();
    }

    template<typename lock>
    void lockContension(lock &l)
    {
        std::thread t1(&UTSpinLock::lockAndSleepT<lock>, this, std::ref(l));
        Assert::AreEqual(true, l.isLocked());

        t1.join();
        Assert::AreEqual(false, l.isLocked());
    }

    TEST_METHOD(testLockContension)
    {
        UTSpinLock::lockContension(m_emptySpin);
        UTSpinLock::lockContension(m_yieldingSpin);
        UTSpinLock::lockContension(m_pausingSpin);
    }

    private:
    emptyLock m_emptySpin;
    yieldingLock m_yieldingSpin;
    pausingLock m_pausingSpin;
};
}
jhammond
  • 1,916
  • 2
  • 15
  • 29
  • Not being familiar with CPPunit, but it would appear the unit test expects a static function; not a member function. Did you give `std::bind` a try ? – WhozCraig Jun 05 '14 at 03:17
  • Could you be more explicit? Where would "bind" go? I apologize if this is a dumb question. – jhammond Jun 05 '14 at 03:18
  • 1
    I don't have (obviously) CPPUnit installed on my winbox (which wouldn't help anyway, as I'm on my Mac), but something to the ilk of passing `std::bind(&UTSpinLock::lockAndSleepT, this, std::placeholders::_1)` as the first param, removing the `this` from your current args list, and sending the the lock reference as-is for the second param. Sry I don't have a box handy to give that a try. You would need to include `` for this, btw. And it wasn't a dumb question. `std::bind` is sheer magic to all but those that use it frequently. – WhozCraig Jun 05 '14 at 03:21
  • A [stupid example](http://coliru.stacked-crooked.com/a/926aed04b121f570) of what I mean. Hope it makes sense. – WhozCraig Jun 05 '14 at 03:32
  • 2
    @WhozCraig [this should not be needed](http://ideone.com/HediqM). – n. m. could be an AI Jun 05 '14 at 03:36
  • @n.m. I didn't think so either. It seems odd that `std::bind` works but the simplified method does not. Do you see something I didn't? I was surprised the OP's original code didn't work, but it wouldn't be the first time I missed something obvious. – WhozCraig Jun 05 '14 at 03:44
  • Btw: You're not alone on this. [**See here**](http://stackoverflow.com/questions/21617860/std-thread-call-template-member-function-of-template-class-compiler-error) for the same problem, but solved and entirely different way using a lambda. The original problem (why this pukes on VC) remained unresolved on that question as well. *Very* nice you included that toolchain mention in your question, btw. Thanks for that. – WhozCraig Jun 05 '14 at 03:53

2 Answers2

2

First of all, this is certainly a bug in the MSVC implementation. It seems to be having trouble when the first argument to std::thread is a pointer to a member function template. On my machine, the 64-bit compiler produces the same error message, while the 32-bit compiler crashes. Thankfully, you can work around this in quite a few ways, all involving not directly pass a pointer to a member function template to thread.


Option 1 - as you've discovered, creating a bind expression and passing that to thread works.


Option 2 - rewrite the class so that it is a template, and the member functions are not.

template<typename lock>
struct UTSpinLock
{
public:
    void lockAndSleepT(lock &l)
    {}

    void lockContension(lock &l)
    {
        std::thread t1(&UTSpinLock::lockAndSleepT, this, std::ref(l));

        t1.join();
    }
};

Option 3 - leave the class definition unchanged, and wrap the pointer to member function template in std::mem_fn

std::thread t1(std::mem_fn(&UTSpinLock::lockAndSleepT<lock>), this, std::ref(l));

Option 4 - no change to the class definition again, this time pass a lambda expression to thread

std::thread t1([&, this](){ lockAndSleepT(l); });
Praetorian
  • 106,671
  • 19
  • 240
  • 328
1

Special thank to WhozCraig! I would accept his answer, but, for some reason, I can't. For those that are interested, I changed:

template<typename lock>
void lockContension(lock &l)
{
    std::thread t1(&UTSpinLock::lockAndSleepT<lock>, this, std::ref(l));
    Assert::AreEqual(true, l.isLocked());

    t1.join();
    Assert::AreEqual(false, l.isLocked());
}

To:

template<typename lock>
void lockContension(lock &l)
{
    auto bounded = std::bind(&UTSpinLock::lockAndSleepT<lock>, this,     std::placeholders::_1);
    std::thread t1(bounded, std::ref(l));
    Assert::AreEqual(true, l.isLocked());

    t1.join();
    Assert::AreEqual(false, l.isLocked());
}
jhammond
  • 1,916
  • 2
  • 15
  • 29
  • I'm genuinely curious why your original code wasn't functional like the sample that @n.m. posted. I wouldn't be terribly shocked if it was due to VC++ perpetual lagging to standard compliance, but still, it seems odd. Anyway, glad you have something that works. Maybe he sees something that I didn't. – WhozCraig Jun 05 '14 at 03:42