0

i have the next code listing-6.1.cpp:

#include <stack>
#include <mutex>
#include <memory>
#include <future>
#include <iostream>
#include <exception>
using namespace std;

struct empty_stack: std::exception
{
const char* what() const throw();
};


template<typename T>
class threadsafe_stack
{
private:
    std::stack<T> data;
    mutable std::mutex m;
public:
    threadsafe_stack(){}
    threadsafe_stack(const threadsafe_stack& other)
    {
        std::lock_guard<std::mutex> lock(other.m);
        data=other.data;
    }
    threadsafe_stack& operator=(const threadsafe_stack&) = delete;
    void push(T new_value)
    {
        std::lock_guard<std::mutex> lock(m);
        data.push(std::move(new_value));
    }
    std::shared_ptr<T> pop()
    {
        std::lock_guard<std::mutex> lock(m);
        if(data.empty()) throw empty_stack();
        std::shared_ptr<T> const res(
            std::make_shared<T>(std::move(data.top())));
        data.pop();
        return res;
    }
    void pop(T& value)
    {
        std::lock_guard<std::mutex> lock(m);
        if(data.empty()) throw empty_stack();
        value=std::move(data.top());
        data.pop();
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lock(m);
        return data.empty();
    }
};


struct Msg {
    size_t a;size_t b;size_t c;size_t d;
};

bool isCorrupted(const Msg& m) {
    return !(m.a == m.b && m.b == m.c && m.c == m.d);
}




int main()
{
    threadsafe_stack<Msg> stack;

    auto prod = std::async(std::launch::async, [&]() {
        for (size_t i = 0; i < 1000000; ++i){
            Msg m = { i, i, i, i };
            stack.push(m);
            //std::this_thread::sleep_for(std::chrono::microseconds(1));
            if (i % 1000 == 0) {
                std::cout << "stack.push called " << i << " times " << std::endl;
            }
        }
    });

    auto cons = std::async(std::launch::async, [&]() {
        for (size_t i = 0; i < 1000000; ++i){
            try {
                Msg m;
                stack.pop(m);
                if (isCorrupted(m)) {
                    std::cout << i <<" ERROR: MESSAGE WAS CORRUPED:" << m.a << "-" << m.b << "-" << m.c << "-" << m.d << std::endl;
                }
                if (i % 1000 == 0) {
                    std::cout << "stack.pop called " << i << " times " << std::endl;
                }
            }
            catch (empty_stack e) {
                std::cout << i << " Stack was empty!" << std::endl;
            }
        }
    });

    prod.wait();
    cons.wait();

    return 0;
}

After it is compile with:

g++ -o exe.out listing-6.1.cpp -pthread -lboost_system -lboost_thread -std=c++0x

I have the next result:

/tmp/ccqQnqlf.o: In function empty_stack::empty_stack(empty_stack const&)': listing-6.1.cpp:(.text._ZN11empty_stackC2ERKS_[_ZN11empty_stackC5ERKS_]+0x1d): undefined reference tovtable for empty_stack' /tmp/ccqQnqlf.o:(.gcc_except_table+0x20): undefined reference to typeinfo for empty_stack' /tmp/ccqQnqlf.o: In functionempty_stack::empty_stack()': listing-6.1.cpp:(.text._ZN11empty_stackC2Ev[_ZN11empty_stackC5Ev]+0x16): undefined reference to vtable for empty_stack' /tmp/ccqQnqlf.o: In functionthreadsafe_stack::pop(Msg&)': listing-6.1.cpp:(.text._ZN16threadsafe_stackI3MsgE3popERS0_[_ZN16threadsafe_stackI3MsgE3popERS0_]+0x53): undefined reference to typeinfo for empty_stack' /tmp/ccqQnqlf.o: In functionempty_stack::~empty_stack()': listing-6.1.cpp:(.text._ZN11empty_stackD2Ev[_ZN11empty_stackD5Ev]+0xb): undefined reference to `vtable for empty_stack' collect2: error: ld returned 1 exit status

sergio campo
  • 190
  • 2
  • 13

4 Answers4

1

You did not define empty_stack::what.

Because that means you didn't define any members of empty_stack, and because it is polymorphic (because std::exception has a virtual destructor), that means no virtual table is created for it, so you get this peculiar-looking error.

If you defined a virtual destructor for empty_stack then you wouldn't get any error about the virtual table:

struct empty_stack : std::exception
{
   ~empty_stack() {}
   const char* what() const throw();
};

but you'd still get a linker error regarding empty_stack::what if you tried to use it.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
1

In most of the cases, LNK2001 the Undefined reference linker errors occurs because of the definition missings.!

In your case, the compiler is not able to locate the definition of public: virtual char const * __thiscall empty_stack::what(void)const "

Try giving appropriate definition to empty_stack::what(void)const
Hope that will work for you.

Shivaraj Bhat
  • 841
  • 2
  • 9
  • 20
0

From the C++ FAQ:

Many compilers put this magical "virtual table" in the compilation unit that defines the first non-inline virtual function in the class. Thus if the first non-inline virtual function in Fred is wilma(), the compiler will put Fred's virtual table in the same compilation unit where it sees Fred::wilma(). Unfortunately if you accidentally forget to define Fred::wilma(), rather than getting a Fred::wilma() is undefined, you may get a "Fred's virtual table is undefined". Sad but true.

(http://www.parashift.com/c++-faq/link-errs-missing-vtable.html)

Thus you need to define empty_stack::what

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Anon
  • 2,608
  • 6
  • 26
  • 38
0

You need to implement

const char* what() const throw();

in the empty_stack class.

But still, be careful using your threadsafe_stack. Especially the method empty. This method is maybe locking the thread, but after returning a status of the stack, the lock is released and the value is hence useless. You need to lock the thread and asking for the status and use the status without releasing the lock.

Example: Thread A is about to pop a value, but thread B is asking for an empty status. B is happy that the stack is not empty, but meanwhile A will pop the last item and the stack is empty. B now has invalid status in hands.

Milan
  • 141
  • 6