4

I'm implementing a simple router in Go. I used to have a lot of redundant code for each endpoint returning an error when the method called wasn't implemented for that endpoint. I refactored and made a "base" type which provides default functions for each request type that simply return the unimplemented error. Now all I have to do is override the specific method functions for a given endpoint I wish to implement. This was all fun and games until I wanted to figure out, given an endpoint variable, which of the methods have been overridden?

Omitting extraneous details, here's as simple of an example as I can think of right now:

package main

import (
    "fmt"
)

// Route defines the HTTP method handlers.
type Route interface {
    Get() string
    Post() string
}

// BaseRoute is the "fallback" handlers,
// if those handlers aren't defined later.
type BaseRoute struct{}

func (BaseRoute) Get() string {
    return "base get"
}

func (BaseRoute) Post() string {
    return "base post"
}

// Endpoint holds a route for handling the HTTP request,
// and some other metadata related to that request.
type Endpoint struct {
    BaseRoute
    URI string
}

// myEndpoint is an example endpoint implementation
// which only implements a GET request.
type myEndpoint Endpoint

func (myEndpoint) Get() string {
    return "myEndpoint get"
}

func main() {
    myEndpointInstance := myEndpoint{URI: "/myEndpoint"}
    fmt.Println(myEndpointInstance.URI)
    fmt.Println(myEndpointInstance.Get())
    fmt.Println(myEndpointInstance.Post())
}

This snippet will print out the following:

/myEndpoint
myEndpoint get
base post

So my overriding of the functions works as intended. Now I'm wondering that in my main function, after I declare the myEndpointInstance, can I tell somehow that the Post function has not been overridden and is still implemented by the underlying BaseRoute without actually calling the function? Ideally, I want something like this:

func main() {
    myEndpointInstance := myEndpoint{URI: "/myEndpoint"}
    if myEndpointInstace.Post != BaseRoute.Post {
        // do something
    }
}

I've played around with the reflect package a bit, but haven't found anything helpful.

icza
  • 389,944
  • 63
  • 907
  • 827
  • Override is an action in compiling phase, so you cloud find it out in your code. Why you should determine it at runtime? – HolaYang Mar 17 '19 at 07:09

1 Answers1

5

As noted by others, which method to call is a compile-time decision. So you can check this at compile time, most IDE's will navigate you to the method that is bound to the actual call.

If you want to check this at runtime, you may compare the function pointers. You can't compare function values, they are not comparable (only to the nil value). Spec: Comparison operators:

Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil.

This is how you can do that:

myEndpointInstance := myEndpoint{URI: "/myEndpoint"}

v1 := reflect.ValueOf(myEndpointInstance.Post).Pointer()
v2 := reflect.ValueOf(myEndpointInstance.BaseRoute.Post).Pointer()
fmt.Println(v1, v2, v1 == v2)

v1 = reflect.ValueOf(myEndpointInstance.Get).Pointer()
v2 = reflect.ValueOf(myEndpointInstance.BaseRoute.Get).Pointer()
fmt.Println(v1, v2, v1 == v2)

This will output (try it on the Go Playground):

882848 882848 true
882880 882912 false

Output tells Post() is not "overridden" (myEndpointInstance.Post is the same as myEndpointInstance.BaseRoute.Post), while Get() is (myEndpointInstance.Get is not the same as myEndpointInstance.BaseRoute.Get).

See related questions:

How to compare 2 functions in Go?

Collection of Unique Functions in Go

icza
  • 389,944
  • 63
  • 907
  • 827