I am experimenting with making pure Haskell-style I/O in C++. It's working correctly, but when I reorganize some definitions, I run into a std::bad_function_call.
This is about as much as it takes to trigger the problem:
//common.h
#include <functional>
#include <iostream>
#include <utility>
#include <string>
class Empty {};
class State {};
template <class A>
class IOMonad {
public:
typedef std::function<std::pair<A, State> (State)> T;
};
template <class A, class B>
const auto bind(typename IOMonad<A>::T ma, std::function<typename IOMonad<B>::T (A)> f) {
return [ma, f] (State state) {
const auto x = ma(state);
return f(x.first)(x.second);
};
}
extern const IOMonad<std::string>::T getLine;
IOMonad<Empty>::T putLine(std::string str);
//externs.cpp
#include "common.h"
const IOMonad<std::string>::T getLine = [](State s) {
(void)s;
std::string str;
std::cin >> str;
return std::make_pair(str, State());
};
IOMonad<Empty>::T putLine(std::string str) {
return [str] (State s) {
(void)s;
std::cout << str;
return std::make_pair(Empty(), State());
};
}
//main.cpp
#include "common.h"
const auto putGet = bind<std::string, Empty>(getLine, putLine);
int main() {
(void)putGet(State());
return 0;
}
With this setup, I get a std::bad_function_call when putGet is called. Previously, I had the contents of externs.cpp in main.cpp between including common.h and defining putGet, and everything worked fine. Something about having those functions in a different translation unit seems to be causing this problem. Also, if I keep the functions in externs.cpp, but I make putGet a local variable to main instead of a global variable, this does not happen. Another thing that makes the exception go away is folding the definition of bind into the definition of putGet, like so:
const auto putGet = [] (State state) {
const auto x = getLine(state);
return putLine(x.first)(x.second);
};
Why is this happening? Does std::function have some limitations I don't know about?