#include <atomic>
#include <cassert>
#include <thread>
#include <type_traits>
#include <utility>

// Make a bitfield class that has a private BaseT bitfield.
// Then, for each bitfield member, make a method that returns a bitfield_member
// for that.
//
// Ex:
// class MyBitfield {
//    unsigned long long bits;
//    using A_type = bitfield_member<unsigned long long, bool, 0, 1>;
//    using A_const = bitfield_member<unsigned long long, const bool, 0, 1>;
//    using B_type = bitfield_member<unsigned long long, unsigned short, 1, 16>
// public:
//    A_type A() {return {bits};}
//    A_const A() const {return {bits};}
//    B_type B() {return {bits};}
//    B_const B() const {return {bits};}
// };
//
template<class BaseT, class FieldT, unsigned bitcount, unsigned offset>
class bitfield_member {
    static_assert(bitcount>0);
    static_assert(bitcount<sizeof(BaseT)*8);
    static_assert(offset<sizeof(BaseT)*8-bitcount);
    
    static const BaseT lomask = static_cast<BaseT>((1ull<<bitcount)-1ull);
    BaseT* bits;
public:
    constexpr explicit bitfield_member(BaseT& bits_) :bits(&bits_) {}
    constexpr operator FieldT() const {return get();}
    constexpr FieldT get() const {return static_cast<FieldT>((*bits>>offset)&lomask);}
    constexpr void set(FieldT value) {
    	assert((value&mask)==value);
    	*bits = (*bits&~(lomask<<offset))|(static_cast<BaseT>(value)<<offset); 
    }
    constexpr bitfield_member& operator=(FieldT value) {set(value); return *this;}
    constexpr bitfield_member& operator++() {set(get()++); return *this;}
    constexpr FieldT operator++(int) {FieldT t=get(); set(get()++); return t;}
    constexpr bitfield_member& operator--() {set(get()--); return *this;}
    constexpr FieldT operator--(int) {FieldT t=get(); set(get()--); return t;}
    constexpr FieldT operator+() const {return get();}
    constexpr FieldT operator-() const {return -get();}
    constexpr FieldT operator!() const {return !get();}
    constexpr FieldT operator~() const {return ~get();}
    constexpr FieldT operator*(FieldT value) const {return get()*value;}
    constexpr bitfield_member& operator*=(FieldT value) {set(get()*value); return *this;}
    constexpr FieldT operator/(FieldT value) const {return get()/value;}
    constexpr bitfield_member& operator/=(FieldT value) {set(get()/value); return *this;}
    constexpr FieldT operator%(FieldT value) const {return get()%value;}
    constexpr bitfield_member& operator%=(FieldT value) {set(get()%value); return *this;}
    constexpr FieldT operator+(FieldT value) const {return get()+value;}
    constexpr bitfield_member& operator+=(FieldT value) {set(get()+value); return *this;}
    constexpr FieldT operator-(FieldT value) const {return get()-value;}
    constexpr bitfield_member& operator-=(FieldT value) {set(get()-value); return *this;}
    constexpr FieldT operator<<(FieldT value) const {return get()<<value;}
    constexpr bitfield_member& operator<<=(FieldT value) {set(get()<<=value); return *this;}
    constexpr FieldT operator>>(FieldT value) const {return get()>>value;}
    constexpr bitfield_member& operator>>=(FieldT value) {set(get()>>=value); return *this;}
    constexpr bool operator<(FieldT value) const {return get()<value;}
    constexpr bool operator<=(FieldT value) const {return get()<=value;}
    constexpr bool operator>(FieldT value) const {return get()>value;}
    constexpr bool operator>=(FieldT value) const {return get()>=value;}
    constexpr bool operator==(FieldT value) const {return get()==value;}
    constexpr bool operator!=(FieldT value) const {return get()!=value;}
    constexpr FieldT operator&(FieldT value) const {return get()&value;}
    constexpr bitfield_member& operator&=(FieldT value) {set(get()&value); return *this;}
    constexpr FieldT operator^(FieldT value) const {return get()^value;}
    constexpr bitfield_member& operator^=(FieldT value) {set(get()^value); return *this;}
    constexpr FieldT operator|(FieldT value) const {return get()|value;}
    constexpr bitfield_member& operator|=(FieldT value) {set(get()|value); return *this;}
};

enum class atomic_bitfield_mutate_step {store, abort, yield_and_retry};
template<class T, class F>
std::pair<bool,T> atomic_bitfield_mutate(std::atomic<T>& atomic, F mutation) {
#if __cpp_lib_is_invocable
    static_assert(std::is_invocable_r_v<atomic_bitfield_mutate_step, F, T&>);
#endif
    T old_value = atomic.load(std::memory_order_consume);
    T store_value;
    do {
        store_value = old_value;
        auto step = mutation(store_value);
        if (step == atomic_bitfield_mutate_step::abort) {return {false, old_value};}
        if (step == atomic_bitfield_mutate_step::yield_and_retry) {std::this_thread::yield(); old_value = atomic.load(std::memory_order_consume); continue;}
    } while(!atomic.compare_exchange_weak(old_value, store_value));
    return {true, store_value};
}

template<class T, class F, class...Args>
T atomic_bitfield_busy_wait_until(std::atomic<T>& atomic, Args&&...params, F is_done_waiting) {
#if __cpp_lib_is_invocable
    static_assert(std::is_invocable_r_v<bool, F, T&, Args...>);
#endif
    T old_value;
    bool is_done;
    do {
        old_value = atomic.load(std::memory_order_consume);
        is_done = is_done_waiting(old_value, std::forward<Args>(params)...);
    } while(!is_done && (std::this_thread::yield(),true));
    return old_value;
}





//USAGE:
class SingleProducerConsumer 
{
	struct bitfield {
        using read_idx_t = bitfield_member<unsigned, unsigned, 8, 0>;
        using write_idx_t = bitfield_member<unsigned, unsigned, 8, 8>;
        using end_idx_t = bitfield_member<unsigned, unsigned, 8, 16>;
        using is_end_found_t = bitfield_member<unsigned, bool, 1, 24>;
        using should_abort_t = bitfield_member<unsigned, bool, 1, 25>;
        
        unsigned value;
        
        read_idx_t read_idx() {return read_idx_t{value};} 
        write_idx_t write_idx() {return write_idx_t{value};}
        end_idx_t end_idx() {return end_idx_t{value};}
        is_end_found_t is_end_found() {return is_end_found_t{value};}
        should_abort_t should_abort() {return should_abort_t{value};}
	};
	
	std::atomic<bitfield> async_flags;
	
    int values[32];
    
    void produce(int value) {
    	unsigned write_idx = async_flags.load(std::memory_order_consume).write_idx();
    	values[write_idx] = value;
    	unsigned next_idx = (write_idx+1)%32;
        std::pair<bool,bitfield> did_advance = atomic_bitfield_mutate(async_flags, [=](bitfield& flags) {
            if (flags.should_abort()) return atomic_bitfield_mutate_step::abort;
            if (next_idx == flags.read_idx()) return atomic_bitfield_mutate_step::yield_and_retry;
            flags.write_idx() = next_idx;
            return atomic_bitfield_mutate_step::store;
        });
    }
    void producer_mark_end() {
    	unsigned idx = async_flags.load(std::memory_order_consume).write_idx();
        unsigned next_idx = (idx + 1) % 32;
        atomic_bitfield_mutate(async_flags, [=](bitfield& flags) {
            if (flags.should_abort()) return atomic_bitfield_mutate_step::abort;
            if (next_idx == flags.read_idx()) return atomic_bitfield_mutate_step::yield_and_retry;
            flags.write_idx() = next_idx;
            flags.end_idx() = idx;
            flags.is_end_found() = true;
            return atomic_bitfield_mutate_step::store;
        });
    }
    bool consumer_at_end() const {
        bitfield flags = async_flags.load(std::memory_order_consume);
        return flags.is_end_found() && flags.read_idx()==flags.end_idx();
    }
    int consumer_read() {
        bitfield flags = atomic_bitfield_busy_wait_until(async_flags, [](bitfield& flags) {
            return flags.read_idx() != flags.write_idx();
        });
        unsigned idx = flags.read_idx();
        return values[idx];
    }
    void consumer_advance() {
        std::pair<bool,bitfield> r = atomic_bitfield_mutate(async_flags, [](bitfield& flags) {
            unsigned old_idx = flags.read_idx();
            if (old_idx == flags.write_idx()) return atomic_bitfield_mutate_step::yield_and_retry;
            flags.read_idx() = (old_idx+1) % 32;
            return atomic_bitfield_mutate_step::store;
        });
    }
};

#include <iostream>
int main() {
	SingleProducerConsumer t;
}