1

I was following a talk on YouTube by Kelvin Henney based on the idiom of Functional C++... About 50 minutes into the video he starts showing an example class structure that he named channel. Then he writes the simple fizzbuzz function and is going to pass that into a server like piece of code using threads. I'm using the code from his video which can be found here: Kevlin Henney - Functional C++

However, when I try to compile the program, Visual Studio is generating the C2661 compiler error pointing to std::tuple... which is coming from the std::tread's constructor within my code.

main.cpp

#include <iostream>
#include <exception>
#include <string>
#include <thread>

#include "server.h"

std::string fizzbuzz(int n) {
    return
        n % 15 == 0 ? "FizzBuzz" :
        n % 3 == 0 ? "Fizz" :
        n % 5 == 0 ? "Buzz" :
        std::to_string(n);
}

void fizzbuzzer(channel<int> & in, channel<std::string> & out) {
    for (;;)
    {
        int n;
        in.receive(n);
        out.send(fizzbuzz(n));
    }
}    

int main() {
    try {
        channel<int> out;
        channel<std::string> back;
        std::thread fizzbuzzing(fizzbuzzer, out, back);
        for (int n = 1; n <= 100; ++n) {
            out << n;
            std::string result;
            back >> result;
            std::cout << result << "\n";
        }    
    }
    catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }    
    return EXIT_SUCCESS;
}

server.h

#pragma once

#include <condition_variable>
#include <queue>
#include <mutex>
#include <iostream>

template<typename ValueType>
class receiving;

template<typename ValueType>
class channel {
private:
    std::mutex key;
    std::condition_variable_any non_empty;
    std::queue<ValueType> fifo;
public:
    void send(const ValueType & to_send) {
        std::lock_guard<std::mutex> guard(key);
        fifo.push(to_send);
        non_empty.notify_all();
    }

    bool try_receive(ValueType & to_receive) {
        bool received = false;
        if (key.try_lock()) {
            std::lock_guard<std::mutex> guard(key, std::adopt_lock);
            if (!fifo.empty()) {
                to_receive = fifo.front();
                fifo.pop();
                received = true;
            }
        }
        return received;
    }

    void receive(ValueType & to_receive) {
        std::lock_guard<std::mutex> guard(key);
        non_empty.wait(
            key,
            [this] {
            return !fifo.empty();
        });
        to_receive = fifo.front();
        fifo.pop();
    }

    void operator<<(const ValueType & to_send) {
        send(to_send);
    }
    receiving<ValueType> operator>>(ValueType & to_receive) {
        return receiving(this, to_receive);
    }    
};

template<typename ValueType>
class receiving {
private:
    channel<ValueType> * that;
    ValueType & to_receive;    
public:
    receiving(channel<ValueType> * that, ValueType & to_receive)
        : that(that), to_receive(to_receive)
    {}
    receiving(receiving && other)
        : that(other.that), to_receive(other.to_receive)
    {
        other.that = nullptr;
    }
    operator bool() {
        auto from = that;
        that = nullptr;
        return from && from->try_recieve(to_receive);
    }
    ~receiving() {
        if (that)
            that->receive(to_receive);
    }
};

I know that the code he is showing is only an example code, but I figured I would try it within my IDE while following the video to get a better understanding of his talk. I'd like to be able to compile this just to see the generated output, and to be able to step through the debugger and disassembler, but I've hit a roadblock at this point. I understand the generated compiler error, just not sure how to resolve it based on his code sample...

Here's the compiler error that is being generated:

1>------ Build started: Project: Computations, Configuration: Debug Win32 ------
1>main.cpp
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\memory(2539): error C2661: 'std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>::tuple': no overloaded function takes 3 arguments
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\thread(46): note: see reference to function template instantiation 'std::unique_ptr<std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>,std::default_delete<_Ty>> std::make_unique<std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>,void(__cdecl &)(channel<int> &,channel<std::string> &),channel<int>&,channel<std::string>&,0>(void (__cdecl &)(channel<int> &,channel<std::string> &),channel<int> &,channel<std::string> &)' being compiled
1>        with
1>        [
1>            _Ty=std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>
1>        ]
1>c:\users\skilz99\source\repos\computations\computations\main.cpp(31): note: see reference to function template instantiation 'std::thread::thread<void(__cdecl &)(channel<int> &,channel<std::string> &),channel<int>&,channel<std::string>&,void>(_Fn,channel<int> &,channel<std::string> &)' being compiled
1>        with
1>        [
1>            _Fn=void (__cdecl &)(channel<int> &,channel<std::string> &)
1>        ]
1>Done building project "Computations.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • Also, if I change it to build for x64 it still gives the same compiler error... – Francis Cugler Jun 28 '20 at 04:31
  • I think it's going to be non-copyable `mutex`, but that's a crappy error message – user4581301 Jun 28 '20 at 04:51
  • Nope. I'm wrong. – user4581301 Jun 28 '20 at 04:53
  • @user4581301 Maybe, but I'm also seeing where `std::unique_ptr` is showing up in the generated compiler error too... and `std::unique_ptr` is non-copyable if I remember correctly unlike `std::shared_ptr`... but that shouldn't have anything to do with not resolving the 3 arguments... then again it might... – Francis Cugler Jun 28 '20 at 04:53
  • Thought I had a simpler example, but it turns out I've been tracking something else – user4581301 Jun 28 '20 at 04:56
  • `thread` take parameters by value and you can't copy the `mutex` or the `condition_variable`, so that's a bug, but I don't see how that contributes to the error you're seeing. – user4581301 Jun 28 '20 at 05:10
  • 3
    `std::thread fizzbuzzing(fizzbuzzer, std::ref(out), std::ref(back));` – StoryTeller - Unslander Monica Jun 28 '20 at 05:11
  • Simplified example: https://godbolt.org/z/bZJzJq and the error goes away if you make `channel` copyable or pass it by reference, but that is one seriously inscrutable error message. – user4581301 Jun 28 '20 at 05:28
  • @StoryTeller-UnslanderMonica Okay, using the `std::ref` wrapper got rid of the compiler error. I'm able to compile and run the program... However, it is not bug-free after `99`, `Fizz`, `Buzz` are printed, the debugger invokes a debug error stating `abort()` has been called and the application exits with an error code of `3`... But as I said, it's only `sample` code, so not a real big deal! – Francis Cugler Jun 28 '20 at 05:42
  • It's just good practice to get a better understanding of the different idioms in programming in general, not just in C++. This was an interesting presentation. The concept of functional programming as opposed to other idioms and paradigms... {object oriented, procedural, generic, modular, observer patterns, etc...} within C++ was informative to hear about. This is definitely not production code, but maybe I can take away some of the concepts that were presented within this video... they could be useful in specific contexts of use. – Francis Cugler Jun 28 '20 at 05:50
  • 1
    You need to join the thread before it goes out of scope, that may fix the abort – Alan Birtles Jun 28 '20 at 08:23
  • @AlanBirtles Okay, that makes sense and since I'm not real familiar with using threads... would I join it before or after the for loop? – Francis Cugler Jun 28 '20 at 17:45
  • 1
    After presumably, when you want to wait for the thread to finish executing – Alan Birtles Jun 28 '20 at 20:48
  • @AlanBirtles Okay, I thought so but wanted to make sure! – Francis Cugler Jun 28 '20 at 21:11
  • 1
    Has your problem been solved? If not, I think this [link](https://stackoverflow.com/questions/33435194/stdthread-initialization-constructor-gives-compile-error) may help you. – Barrnet Chou Jun 29 '20 at 08:29

0 Answers0