11 Replies - 467 Views - Last Post: 08 November 2017 - 04:55 PM Rate Topic: -----

#1 bb8  Icon User is offline

  • D.I.C Regular

Reputation: 6
  • View blog
  • Posts: 307
  • Joined: 31-January 16

virtual function to return different types

Posted 04 November 2017 - 10:46 AM

i have a situation like this (i know that maybe this is not the best way to do this so if you have any suggestions, please let me know):
#include <iostream>
#include <cstdint>
#include <variant>
#include <any>

struct ProgramCounter : Register {
    ProgramCounter(unsigned int line_number) : Register(line_number) {}

    unsigned int operator*() const { 
        try {
	    return std::get<unsigned int>(m_data);
	} catch(const std::bad_variant_access &e) {
	    std::cerr << e.what() << std::endl;
	}
    }
};

struct ByteRegister : Register {
    ByteRegister(char data) : Register(data) {}

    char operator*() const { 
	try {
            return std::get<char>(m_data);
	} catch(const std::bad_variant_access &e) {
	    std::cerr << e.what() << std::endl;
	}
    }
};

struct Zero : Register {
    Zero() : Register(static_cast<std::uint64_t>(0)) {}

    std::uint64_t operator*() const {
	return static_cast<std::uint64_t>(0);
    }
};

int main(int argc, char **argv) {

    ProgramCounter *pc = new ProgramCounter(5);
    std::cout << pc->operator*() << '\n';

    ByteRegister *byte = new ByteRegister('e');
    std::cout << byte->operator*() << '\n';

    Zero *zero = new Zero;
    std::cout << zero->operator*() << '\n';

    return 0;
}


this code works, good or bad, it does. but this is not what i want. i.e. what if i wanna keep the registers via base pointer? i can't do that (operator* isn't a base class member function)

so i want the dereference operator to be a virtual function in base class. however, i don't know how to do that. i thought of returning std::any, like this:
#include <iostream>
#include <cstdint>
#include <variant>
#include <any>

struct Register {
    Register(std::variant<unsigned int, char, std::uint64_t> data) : m_data(data) {}

    std::variant<unsigned int, char, std::uint64_t> m_data;
    virtual std::any operator*() const = 0;
};

struct ProgramCounter : Register {
    ProgramCounter(unsigned int line_number) : Register(line_number) {}

    std::any operator*() const { 
	try {
     	    return std::any_cast<unsigned int>(std::get<unsigned int>(m_data));
	} catch(const std::bad_variant_access &e) {
    	    std::cerr << e.what() << std::endl;
	}
    }
};

struct ByteRegister : Register {
    ByteRegister(char data) : Register(data) {}

    std::any operator*() const {
	try {
    	    return std::any_cast<char>(std::get<char>(m_data));
	} catch(const std::bad_variant_access &e) {
   	    std::cerr << e.what() << std::endl;
	}
    }
};

struct Zero : Register {
    Zero() : Register(static_cast<std::uint64_t>(0)) {}

    std::any operator*() const {
	return static_cast<std::uint64_t>(0);
    }
};

int main(int argc, char **argv) {

    ProgramCounter *pc = new ProgramCounter(5);
    std::cout << std::any_cast<unsigned int>(pc->operator*()) << '\n';

    ByteRegister *byte = new ByteRegister('e');
    std::cout << std::any_cast<char>(byte->operator*()) << '\n';

    Zero *zero = new Zero;
    std::cout << std::any_cast<uint64_t>(zero->operator*()) << '\n';

    return 0;
}


but i don't like this solution at all, cause it has too many casts.

p.s. i can only call operator*() directly, like i did in main()... why?

so please advise me some beautiful solution to this.
thanks

Is This A Good Question/Topic? 0
  • +

Replies To: virtual function to return different types

#2 #define  Icon User is offline

  • Duke of Err
  • member icon

Reputation: 1852
  • View blog
  • Posts: 6,661
  • Joined: 19-February 09

Re: virtual function to return different types

Posted 04 November 2017 - 11:13 AM

The derived structs all have a specified type. Could Register be a templated struct?
Was This Post Helpful? 1
  • +
  • -

#3 bb8  Icon User is offline

  • D.I.C Regular

Reputation: 6
  • View blog
  • Posts: 307
  • Joined: 31-January 16

Re: virtual function to return different types

Posted 04 November 2017 - 11:38 AM

thanks, that helped, i did this:
template<typename T>
struct Register {
    Register(const T &data = T()) : m_data(data) {}
    T operator*() const { return m_data; }

    T m_data;
};

using ProgramCounter = Register<unsigned int>;
using ByteRegister = Register<char>;
using Zero = Register<std::uint64_t>;

however, if i want to keep them using the base type, i have to write (maybe this is not a problem at all):
Register<unsigned int> *pc = new ProgramCounter(5);

but i'm still curious why, say, std::cout << *pc << '\n'; would give an error:
error C2679: binary '<<': no operator found which takes a right-hand operand of type 'Register<unsigned int>' (or there is no acceptable conversion)

but the dereferencing should have given a value of type T, right?

EDIT: and another question tortures me: how can i make sure that any other types won't be used with the template base class (for example, Register<int>)? well, i could declare the template class in one header file, then include it in another header where i'll define the usings, and when i'm actually using these, i'll include that latter header file. but... a better solution?

This post has been edited by bb8: 04 November 2017 - 12:36 PM

Was This Post Helpful? 0
  • +
  • -

#4 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2511
  • View blog
  • Posts: 3,983
  • Joined: 21-June 11

Re: virtual function to return different types

Posted 04 November 2017 - 01:33 PM

pc is a pointer. Using * on it gives you the Register object that the pointer points to. It does not invoke the *-operator of the Register class, it invokes the * operator of the pointer. To invoke Register's operator*, you'll need an additional *.
Was This Post Helpful? 0
  • +
  • -

#5 bb8  Icon User is offline

  • D.I.C Regular

Reputation: 6
  • View blog
  • Posts: 307
  • Joined: 31-January 16

Re: virtual function to return different types

Posted 04 November 2017 - 11:22 PM

and how can i make sure that, for example, Register<int> won't be created?
Was This Post Helpful? 0
  • +
  • -

#6 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 5890
  • View blog
  • Posts: 20,102
  • Joined: 05-May 12

Re: virtual function to return different types

Posted 05 November 2017 - 08:14 AM

One approach is go back to some of your older threads where you implemented template specialization. For the types that you don't support, you could create a specialization where the constructor is private, or throws an exception.

For other approaches, see this StackOverflow question.
Was This Post Helpful? 1
  • +
  • -

#7 bb8  Icon User is offline

  • D.I.C Regular

Reputation: 6
  • View blog
  • Posts: 307
  • Joined: 31-January 16

Re: virtual function to return different types

Posted 05 November 2017 - 01:22 PM

Skydiver, wow! you remember what questions i've asked?! that was literally a year ago! i'm pretty impressed!

and well, i remember asking it too :D but the difference between that case and this one is, that in the old case, i derived from base template, and here i'm just using aliases to define "specializations".

i had tried deriving from the base class here, but for that i need to have one templated derived class and one - a specialized one, for each of the 3 register subclasses. i couldn't even do that cause i got strange errors:
#include <iostream>
using namespace std;

template<typename T>
struct Reg {};

template<typename T>
struct PC : Reg<T> {};

template<>
struct PC : Reg<unsigned int> {};

int main() {
    PC pc;
    return 0;
}

will give:
prog.cpp:11:8: error: template specifiers not specified in declaration of ‘template<class T> struct PC’
 struct PC : Reg<unsigned int> {};
        ^~
prog.cpp: In function ‘int main()’:
prog.cpp:16:4: error: missing template arguments before ‘pc’
 PC pc;
    ^~



i like the idea of have a private ctor in base. that works. fine: (ugly code because i'm on my phone)
#include <iostream>
using namespace std;

template<typename T>
struct Reg {private:Reg(){}};

template<>
struct Reg<unsigned int> {Reg(){cout<<1;}};

using PC = Reg<unsigned int>;

int main() {
    PC pc;
    Reg<int> reg; // error
    return 0;
}


but i also want to have implemented the solution mentioned in the answers of the linked so question, that is to say, using static_assert but i can't figure out what should the condition be in this case
Was This Post Helpful? 0
  • +
  • -

#8 #define  Icon User is offline

  • Duke of Err
  • member icon

Reputation: 1852
  • View blog
  • Posts: 6,661
  • Joined: 19-February 09

Re: virtual function to return different types

Posted 07 November 2017 - 02:25 PM

Whereabouts are you planning to use the static_assert?
Was This Post Helpful? 0
  • +
  • -

#9 bb8  Icon User is offline

  • D.I.C Regular

Reputation: 6
  • View blog
  • Posts: 307
  • Joined: 31-January 16

Re: virtual function to return different types

Posted 08 November 2017 - 01:06 PM

#define, i thought i could use it when checking whether the template is specialized for that type but, after all, i think just writing
using ProgramCounter = unsigned int;
is what i'll go for. simple and redundant-check-free.
Was This Post Helpful? 0
  • +
  • -

#10 #define  Icon User is offline

  • Duke of Err
  • member icon

Reputation: 1852
  • View blog
  • Posts: 6,661
  • Joined: 19-February 09

Re: virtual function to return different types

Posted 08 November 2017 - 01:27 PM

I thought it might be used in the constructor of the unsupported types.
Was This Post Helpful? 0
  • +
  • -

#11 bb8  Icon User is offline

  • D.I.C Regular

Reputation: 6
  • View blog
  • Posts: 307
  • Joined: 31-January 16

Re: virtual function to return different types

Posted 08 November 2017 - 01:45 PM

hmm. what i was saying was that assert could be used in base template class and check whether the type is one of the supported types (since one could only create an unsupported Register only using the Register template class since the other ones are fully specializations).

but, how could i use assert to check for unsupported types? (or we're saying the same thing? are you saying the same thing as i am, negated?)
Was This Post Helpful? 0
  • +
  • -

#12 #define  Icon User is offline

  • Duke of Err
  • member icon

Reputation: 1852
  • View blog
  • Posts: 6,661
  • Joined: 19-February 09

Re: virtual function to return different types

Posted 08 November 2017 - 04:55 PM

If the type is not supported, the derived class will not be a specialized class. In that case the general case's constructor could be used - for example to print an error. Note the compiler chooses the class object based on the type, so testing of the type is not required.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1