Danilo
Danilo

Reputation: 1028

What is a proper way to initialize object on stack or on heap?

tl;dr; What is a proper way to initialize object on stack or on heap?

I want to make a profiling tool that can track time for iterative purposes and for single measurements - so that I can use same object to measure average time for single function (called N times) but also to see how functions behave in complex project.

Now, I might be wrong, but the way I see it this object needs to be able to be initialized with reference (if there is global tracker) and independently on heap ( if its one use). I tried to mangle something that could work as MRE, so please consider this code:

struct test{
    
    test(const char *caller, int l )
    :caller(caller),line(l),pntr(nullptr),flag(0)
    {
        std::clog << "instantiated as heap" << std::endl;
        this->make_heap();
    }
    
    test(const test & other)
    {
        std::clog << "copying\t";
        this->caller = other.caller;
        this->line = other.line;
        if( other.is_as_referenece())
        {
            std::clog << "as reference\n";
            this->pntr = other.pntr; // multiple pointers to same variable??
            this->flag = other.flag;
        }
        else
        {
            std::clog << "as heap\n";
            this->pntr = new unsigned(*other.pntr);
            this->flag = other.flag;
        }
    }
    
    test( test && other)
    {
        std::clog << "moving\t";
        this->caller = other.caller;
        this->line = other.line;
        other.caller = "\0"; // moved, thus no caller 
        other.line = 0; // moved, thus no line number
        
        if( other.is_as_referenece())
        {
            std::clog << "as reference\n";
            this->pntr = other.pntr;
            this->flag = other.flag;
            other.unlink_reference();
        }
        else
        {
            std::clog << "as heap\n";
            this->pntr = new unsigned(*other.pntr);
            this->flag = other.flag;
            other.delete_heap();
        }
        
    }
    
    operator bool() { return this->line != 0;} // no line number 0 
    
    test &operator=(const test &other)
    {
        std::clog << "copying\t";
        this->caller = other.caller;
        this->line = other.line;
        if( other.is_as_referenece()) 
        {
            std::clog << "as reference\n";
            this->pntr = other.pntr; // not quite sure about this, 
            this->flag = other.flag;
        }
        else
        {
            std::clog << "as heap\n";
            this->pntr = new unsigned(*other.pntr);
            this->flag = other.flag;
        }
        return *this;
    }
    test &operator=( test && other)
    {
        std::clog << "moving\t";
        this->caller = other.caller;
        this->line = other.line;
        other.caller = "\0";
        other.line = -1;
        
        if( other.is_as_referenece()) // if other is referenced, swap pointers 
        {
            std::clog << "as reference\n";
            this->pntr = other.pntr;
            this->flag = other.flag;
            other.unlink_reference();
        }
        else // if others is heap allocated, make a new heap copy
        {
            std::clog << "as heap\n";
            this->pntr = new unsigned(*other.pntr);
            this->flag = other.flag;
            other.delete_heap();
        }
        return *this;
    }
    test &operator=( unsigned &ref) // normally it generates heap, unless by operator reference is assigned 
    {
        if( this->is_on_heap()) // checks if its heap allocated
        {
            this->delete_heap(); // if is, deletes heap 
        }
        this->link_reference(ref);
        return *this;
    }
    
    ~test()
    {
        std::clog << "deletion proccess" << std::endl;
        if(this->pntr != nullptr) // if not pointing to NULL 
        {
            if(!this->delete_heap()) // try to delete heap, 
            {
                this->unlink_reference(); // if fails, unlink object 
            }
        }    
        
    }
    
    const char *caller; // function name 
    unsigned line; // line number timer is called from 
    
    bool is_on_heap() const // state informers 
    {
        return (flag & 0x1) == 0x1;
    }
    
    bool is_as_referenece() const // state informers 
    {
        return (flag & 0x2) == 0x2;
    }
    
    friend std::ostream &operator<<(std::ostream &, const test &); // for testing purposes 
    
    protected:
        
        bool link_reference(unsigned &value) // links object that is reffered
        {
            if( this->is_on_heap()) {return false;}
            std::cout << "linking reference" << std::endl;
            this->pntr = &value;
            this->set_as_reff();
            return true;
        }
        
        bool unlink_reference() // unlinks object that is reffered
        {
            if( this->is_on_heap()) {return false;}
            std::cout << "unlinking reference" << std::endl;
            this->pntr = nullptr;
            this->clear_as_reff();
            return true;
        }
        
        bool make_heap()  // makes object on heap
        {
            if( this->is_as_referenece() ) { return false;}
            std::cout << "making heap" << std::endl;
            this->pntr = new unsigned(0);
            this->set_as_heap();
            return true;
        }
        
        bool delete_heap() // deletes object if its on heap
        {
            if( this->is_as_referenece() ) {return false;}
            std::cout << "deleting heap" << std::endl;
            delete this->pntr;
            this->pntr = nullptr;
            this->clear_on_heap();
            return true;
        }
        
    private:
        unsigned *pntr = nullptr;   // main data holder.
        uint8_t flag = 0;       // state holder, tracks if object is on heap or reference
        
        // main state changers 
        void set_as_heap()    { flag |= 0x1;}
        void clear_on_heap()    { flag &= 0xfe;}
        void set_as_reff()    { flag |= 0x2;}
        void clear_as_reff()    { flag &= 0xfd;}
            
    
};

std::ostream &operator<<(std::ostream &os, const test &t)
{
    
    if( t.pntr == nullptr)
    {
        return os << "NOT THERE" << '\t' << t.line << ':' << t.caller;
    }    
    
    if( t.is_as_referenece())
    {
        os << "REFF: " << t.pntr << ' ' << *t.pntr; 
    }
    else
    {
        os << "HEAP: "<< t.pntr << ' ' << *t.pntr; 
    }
    return os << '\t' << t.line << ':' << t.caller;
}

#define TESTER_HEAP test temp = test(__FUNCTION__,__LINE__);

Where to gain function call and line (for g++ (C++11.4), Ubuntu) I need a macro to initialize it to correctly place in source. Now with scope testing, valgrind and gdb have no issues:

void test_macro()
{
    unsigned value = 123;    
    
    {
    TESTER_HEAP
    std::cout << temp << std::endl;
    }
    
    std::cout << std::endl;
    
    {
    TESTER_HEAP
    temp = value;
    std::cout << temp << std::endl;
    }
    
    std::cout << std::endl;
    
    {
    TESTER_HEAP;
    test temp2 = temp;
    std::cout << temp << std::endl;    
    std::cout << temp2 << std::endl;
    }
    
    std::cout << std::endl;
    
    {
    TESTER_HEAP;
    test temp2 = (test &&)temp;
    std::cout << temp << std::endl;    
    std::cout << temp2 << std::endl;
    }
}

I tried googling this and it is possible that due to some terminology i simply wasn't searching for right things since search was unhelpful. I am sure there are plethora of people who are more experienced and smarter than me who had tried something similar. So can kind people of SE help by pointing to right direction or sharing how they approached heap and stack allocation within same object?

Upvotes: 0

Views: 92

Answers (0)

Related Questions