As I understand it the type declaration works like kind_of…
“So types aren't strictly the type named but rather act like a is_a? or kind_of?.” https://github.com/crystal-lang/crystal/issues/4232
So I think that part of this code works.
But when I use a macro to define an instance method on an inherited class the error message complains about the method missing from the wrong object.
class Base
macro inherited
def name
"Joe Smith"
end
end
end
class Context < Base; end
class Render
def initialize(#inner_context : Base); end
def display
name
end
forward_missing_to inner_context
private property inner_context
end
puts Render.new(Context.new).display
And the output is:
Error in line 23: instantiating 'Render#display()'
in line 15: instantiating 'name()'
in macro 'forward_missing_to' /usr/lib/crystal/object.cr:1132, line 1:
> 1. macro method_missing(call)
2. inner_context.{{call}}
3. end
4.
expanding macro
in macro 'method_missing' expanded macro: forward_missing_to:1, line 1:
> 1. inner_context.name
2.
undefined method 'name' for Base (compile-time type is Base+)
What am I missing here?
#inner_context could be of type Base, which doesn't define a base method.
One solution is to mark Base as abstract: abstract class Base.
Related
Like I said in the title, I can't pass an inner class object as argument for outer class' constructor. Below is the class header;
class Corner {
public:
Corner(cv::Mat imageMat, int flag, feat::Corner::cornerDetectorHarris* cdh = nullptr);
...
class cornerDetectorHarris {...};
...
};
Visual Studio Community has no problem with the codeabove. Problem is when I try to define the function in .cpp file;
feat::Corner::Corner(cv::Mat imageMat, int flag, feat::Corner::cornerDetectorHarris* cdh) {}
VSC claims a E0493 error under the second Corner, "no instance of overloaded function "feat::Corner::Corner" matches the specified type". Here is the error code;
Severity Code Description Project File Line Suppression State
Error (active) E0493 no instance of overloaded function "feat::Corner::Corner" matches the specified type bpv2 C:\Users\ASUS\source\repos\bpv2\bpv2\feat.cpp 533
If I remove the cornerDetectorHarris pointer the error goes away so I'm fairly sure it's the problem. I tried removing the default value for argument but it didn't change anything.
Simply declare the inner class before any methods that depend on it. You can of course even define the methods in the header if you so wish.
Not really sure why you would do it this way though; why not simply have cornerDetectorHarris declared outside and before Corner?
There is some useful discussion on the topic here:
Why would one use nested classes in C++?
class Corner {
public:
class cornerDetectorHarris {...};
Corner(cv::Mat imageMat, int flag, feat::Corner::cornerDetectorHarris* cdh = nullptr)
{
//do stuff
}
// etc
};
If I have a function which accepts an argument of multiple types, how can I enforce that the return must match the value of the input?
This comes up particularly when I want a method to work with any children of a parent type. For demonstration, consider something that is "barlike":
abstract struct Barlike
property bar: Int32
def initialize(#bar); end
end
abstract def make_clang_sound
abstract def serve_drinks
end
Now any struct can implement those two methods, and store that value
struct Bar1 < Barlike
def make_clang_sound
puts "bing bang bong"
end
def serve_drinks
puts "your drink sir"
end
end
struct Bar2 < Barlike
def make_clang_sound
puts "kling klang"
end
def serve_drinks
puts "here are your drinks"
end
end
Now what if I have a method that wants to use the bar and return a new one with an updated value (these are structs afterall):
def foo(arg : Barlike)
new_bar = arg.bar + 2
arg.class.new(new_bar)
end
this will return a Bar1 if a Bar1 is passed in and a Bar2 if that is passed in but it's not guaranteed:
def foo(arg : Barlike)
"howdy"
end
I'm going to be putting my foo into an abstract structure as well, so I need to guarantee that implementers of foo return the same type of Barlike that was given.
I tried
def foo(arg : Barlike) : arg.class
end
But that's a compile time error (arg cannot be used there like that)
I also tried
def foo(arg : Barlike) : typeof(arg)
end
which passes, but typeof here is just Barlike whereas I really need it to be only the thing that was passed in, only Bar1 or Bar2 and so on.
Can macros help?
The tool for this are free variables. That's essentially generics scoped to a single method.
# This method returns the same type as its argument
def foo(arg : T) : T forall T
arg
end
This would already solve the main part of your question.
However, it is currently not possible to apply type restrictions to free variables, for example restricting T to Barlike.
There are workarounds, though:
Use a macro to validate the argument type:
def foo(arg : T) : T forall T
{% raise "arg must implement Barlike" unless T < Barlike %}
arg
end
Delegate to another method with a type restriction:
def foo(arg : T) : T forall T
foo_impl(arg)
end
private def foo_impl(arg : Barlike)
arg
end
Both workarounds affect the implementation of the method. There is no way to specify such a type restriction for a abstract def. Number 2 might be feasible, if you make foo_impl abstract and require inheriting classes to implement this one, instead of foo.
But it's probably also fine to just go with the initial example using free variables, without the Barlike restriction. In practice, you probably don't gain much.
Here is something that works:
{% for sub in Barlike.subclasses %}
struct {{sub}}
def foo() : {{sub}}
{{sub}}.new(#bar+1)
end
end
{% end %}
full example
But it feels like this is trying to solve the wrong problem. It uses the #subclasses macro to generate a foo for all of the child structs.
You could also declare these as self methods inside the abstract class: example.
The following code works when used without C++ classes (Arduino code);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiA);
This creates a variable midiA with the correct type. The macro definition is:
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
midi::MidiInterface<Type> Name((Type&)SerialPort);
However, I want to use this inside a class (to create a class variable midiA), but I cannot get it to work.
What I have now is:
class MidiHandler
{
midi::MidiInterface<(HardwareSerial&) Serial1)> midiA;
}
However, I get the following error:
MidiHandler.h: 23:39: error: type\value mismatch at argument 1 in template parameter list for 'template<class SerialPort, class _Settings> class midi::MidiInterface
midi*: MidiInterface<((HardwareSerial&)Serial1)> midiA
I think I might need to add the template to the class MidiHandler, also I wonder if I need to initialize it in the constructor.
Disclaimer: I don't have an Arduino to compile and test this, but from the pure C++ point of view, I would declare the member in the class declaration and initialize it in the constructor. Like this:
class MidiHandler
{
public:
MidiHandler(HardwareSerial& serial_port)
: midiA(serial_port)
{ }
private:
midi::MidiInterface<HardwareSerial> midiA;
};
Then, instantiate the class like this:
MidiHandler handler((HardwareSerial&) Serial1);
DETAIL:
The MidiHandler class has a member variable named midiA of type midi::MidiInterface<HardwareSerial>, which is a template with one template parameter (the Type parameter from the macro). HardwareSerial is used as the value for the template parameter.
The type midi::MidiInterface<HardwareSerial> has a constructor requiring one parameter of type HardwareSerial&. Therefore, MidiHandler::MidiHandler, the MidiHandler class constructor, needs a parameter of the same type to be used to construct midiA.
Finally, Serial1 is passed as the constructor parameter to handler. Serial1 must be of type HardwareSerial& or convertible to it. The C stile cast, (HardwareSerial&), may or may not be needed depending on the type of Serial1, but I put it there since the macro had it.
As is shown in the define:
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name)
midi::MidiInterface<Type> Name((Type&)SerialPort);
So substituting in Your values should be:
midi::MidiInterface<HardwareSerial> midiA((HardwareSerial&)Serial1);
Answer as result of the accepted answer by Luis Guzman:
In header file:
midi::MidiInterface<HardwareSerial> midiA;
midi::MidiInterface<HardwareSerial> midiB;
midi::MidiInterface<HardwareSerial> midiC;
In constructor:
MidiHandler::MidiHandler()
: midiA(Serial1),
midiB(Serial2),
midiC(Serial3)
I'm trying to do something like this:
class Shape {
...
...
...
Line GetTangent();
}
class Line:Shape { ... }
It did not work because the type Line had not been declared yet.
What can I do?
Forward your class.
class Line;
class Shape
{
public:
Line GetTangent();
};
class Line : Shape
{
}
Line Shape::GetTangent()
{
// Do sth
}
C++ requires the type to be complete, when being used. In Shape you only notify compiler that you will use Line class, but you don't actually use it. Also, if you - for example - return Line * or Line & (which, in a way, is a pointer too), it should also work, because compiler will work with pointers only (no need to know, what the pointer's target is).
On the other hand, if you try to instantiate Line inside the Shape class, it won't work, because C++ doesn't know, how does Line look, what kind of constructors does it have and so on. So for that to work, you should provide implementation of the GetTangent method after the whole definition of the Line class, as shown in the example.
This is what you will get when trying to use Line inside of Shape: http://ideone.com/YQOjQH
On the other hand, if you extract the definition of GetTangent outside, it will work: http://ideone.com/ZrOgdR
You'll need to forward declare Line and then return it from methods like GetTangent as either a reference or a pointer. You can't return it my value as the compiler won't know how to do it as it hasn't seen the definition of Line.
Returning by pointer or reference is probably going to be a pain, so you might want to rethink your class hierarchy. For example, it'll praboly be easier to not have Line derive from Shape.
Let's say we're given this class with an inner struct.
class Some {
public:
struct Crap{};
Crap Process(Crap& c);
}
Some::Crap Some::Process(Crap& crap) { Crap moreCrap = .. }
It makes sense to me that the return type (Some::Crap) in "Process" is scoped.
It makes sense to me that no scoping is needed inside the body of "Process".
I do not understand why the inner struct parameter of Process (Crap& crap) does not require scoping.
Does anyone have any insight on this?
Thanks
In fact, the parameter type does need to be qualified (unless your example is incorrect and you intend for Process to be a member function of Some).
Assuming the following snippet:
class Some
{
public:
struct Crap{};
Crap Process(Crap&);
};
Some::Crap Some::Process(Crap& crap) { ... }
Basically, Crap does not require scoping inside the function parameter list and body for the same reason you don't need this-> to access data members and member functions: it is implicit because it is at the same scope.
The reason return type is needed is simply because the parser encounters the return type before the method name and cannot (is not required to?) deduce scope at that moment.
The question title says 'member function', but your example contains no member function. Assuming you meant Some::Process rather than just Process:
The class scope for the member function definition begins at the (, not at the {. I assume the reasoning is exactly so that things like this can be typed shorter.