根据类型从泛型中进行动态函数调用

huangapple go评论127阅读模式
英文:

Dynamic function call from generics depending on type

问题

能否使用泛型根据参数类型调用函数?
我会尽量详细解释我的任务。

例如,我有一个带有两个方法的结构体:

  1. package main
  2. import "reflect"
  3. type Cut interface {
  4. int | int8 | int16 | int32 | int64 | float32 | float64 | string
  5. }
  6. type AudioObj struct {
  7. fileName string
  8. }
  9. func (A AudioObj) CutIfFirstIsString(from string, to float64) {
  10. // Cut audio file: ("00:00:03", 81.0)
  11. }
  12. func (A AudioObj) CutIfSecondIsString(from float64, to string) {
  13. // Cut audio file: (0.3, "01:21:00")
  14. }

这是我的泛型函数:

  1. func CutAudio[S Cut, E Cut](MusicFile AudioObj, start S, end E) {
  2. // 第一个参数是字符串。
  3. if reflect.ValueOf(start).Kind() == reflect.String {
  4. MusicFile.CutIfFirstIsString(start, end)
  5. }
  6. // 第二个参数是字符串。
  7. if reflect.ValueOf(end).Kind() == reflect.String {
  8. MusicFile.CutIfSecondIsString(start, end)
  9. }
  10. }

在这里,我尝试根据函数参数类型来剪切音频文件:

  1. func main() {
  2. audio := AudioObj{fileName: "music.mp3"}
  3. CutAudio(audio, "00:00:03", 81.0)
  4. CutAudio(audio, 0.3, "01:21:00")
  5. }

输出:

  1. ./prog.go:24:32: cannot use start (variable of type S constrained by Cut) as type string in argument to MusicFile.CutIfFirstIsString
  2. ./prog.go:24:39: cannot use end (variable of type E constrained by Cut) as type float64 in argument to MusicFile.CutIfFirstIsString
  3. ./prog.go:29:33: cannot use start (variable of type S constrained by Cut) as type float64 in argument to MusicFile.CutIfSecondIsString
  4. ./prog.go:29:40: cannot use end (variable of type E constrained by Cut) as type string in argument to MusicFile.CutIfSecondIsString

PlayGround: https://go.dev/play/p/1jx1-vHXDdn

是否可以将泛型类型转换为我传递给它的类型?

英文:

Is it possible to use generics to call a function depending on the type of parameters?
I will try to explain my task in as much detail as possible.

For example, I have a structure with two methods:

  1. package main
  2. import "reflect"
  3. type Cut interface {
  4. int | int8 | int16 | int32 | int64 | float32 | float64 | string
  5. }
  6. type AudioObj struct {
  7. fileName string
  8. }
  9. func (A AudioObj) CutIfFirstIsString(from string, to float64) {
  10. // Cut audio file: ("00:00:03", 81.0)
  11. }
  12. func (A AudioObj) CutIfSecondIsString(from float64, to string) {
  13. // Cut audio file: (0.3, "01:21:00")
  14. }

And this is my generic:

  1. func CutAudio[S Cut, E Cut](MusicFile AudioObj, start S, end E) {
  2. // The first parameter is a string.
  3. if reflect.ValueOf(start).Kind() == reflect.String {
  4. MusicFile.CutIfFirstIsString(start, end)
  5. }
  6. // The second parameter is a string.
  7. if reflect.ValueOf(end).Kind() == reflect.String {
  8. MusicFile.CutIfSecondIsString(start, end)
  9. }
  10. }

Here I try to cut the audio file depending on function parameters type:

  1. func main() {
  2. audio := AudioObj{fileName: "music.mp3"}
  3. CutAudio(audio, "00:00:03", 81.0)
  4. CutAudio(audio, 0.3, "01:21:00")
  5. }

Output:

  1. ./prog.go:24:32: cannot use start (variable of type S constrained by Cut) as type string in argument to MusicFile.CutIfFirstIsString
  2. ./prog.go:24:39: cannot use end (variable of type E constrained by Cut) as type float64 in argument to MusicFile.CutIfFirstIsString
  3. ./prog.go:29:33: cannot use start (variable of type S constrained by Cut) as type float64 in argument to MusicFile.CutIfSecondIsString
  4. ./prog.go:29:40: cannot use end (variable of type E constrained by Cut) as type string in argument to MusicFile.CutIfSecondIsString

PlayGround: https://go.dev/play/p/1jx1-vHXDdn

It is posibile to convert generic type into the type that I sended to it?

答案1

得分: 1

你可以使用一个简单的类型开关来实现这个,但是代码会有点冗长:

  1. package main
  2. import (
  3. "fmt"
  4. "strconv"
  5. )
  6. type Cut interface {
  7. int | int8 | int16 | int32 | int64 | float32 | float64 | string
  8. }
  9. type AudioObj struct {
  10. fileName string
  11. }
  12. func (A AudioObj) CutIfFirstIsString(from string, to float64) {
  13. // Cut audio file: ("00:00:03", 81.0)
  14. fmt.Println(`Cut audio file: ("00:00:03", 81.0)`)
  15. }
  16. func (A AudioObj) CutIfSecondIsString(from float64, to string) {
  17. // Cut audio file: (0.3, "01:21:00")
  18. fmt.Println(`Cut audio file: (0.3, "01:21:00")`)
  19. }
  20. func CutAudio[S Cut, E Cut](MusicFile AudioObj, start S, end E) {
  21. // 第一个参数是字符串。
  22. if _, ok := any(start).(string); ok {
  23. MusicFile.CutIfFirstIsString(cutToString(start), cutToFloat(end))
  24. }
  25. // 第二个参数是字符串。
  26. if _, ok := any(end).(string); ok {
  27. MusicFile.CutIfSecondIsString(cutToFloat(start), cutToString(end))
  28. }
  29. }
  30. func cutToString[S Cut](cut S) string {
  31. var str string
  32. switch start := any(cut).(type) {
  33. case int:
  34. str = strconv.FormatInt(int64(start), 10)
  35. case int8:
  36. str = strconv.FormatInt(int64(start), 10)
  37. case int16:
  38. str = strconv.FormatInt(int64(start), 10)
  39. case int32:
  40. str = strconv.FormatInt(int64(start), 10)
  41. case int64:
  42. str = strconv.FormatInt(start, 10)
  43. case float32:
  44. str = strconv.FormatFloat(float64(start), 'G', -1, 32)
  45. case float64:
  46. str = strconv.FormatFloat(start, 'G', -1, 64)
  47. case string:
  48. str = start
  49. }
  50. return str
  51. }
  52. func cutToFloat[S Cut](cut S) float64 {
  53. var flt float64
  54. switch end := any(cut).(type) {
  55. case int:
  56. flt = float64(end)
  57. case int8:
  58. flt = float64(end)
  59. case int16:
  60. flt = float64(end)
  61. case int32:
  62. flt = float64(end)
  63. case int64:
  64. flt = float64(end)
  65. case float32:
  66. flt = float64(end)
  67. case float64:
  68. flt = end
  69. case string:
  70. flt, _ = strconv.ParseFloat(end, 64)
  71. }
  72. return flt
  73. }
  74. func main() {
  75. audio := AudioObj{fileName: "music.mp3"}
  76. CutAudio(audio, "00:00:03", 81.0)
  77. CutAudio(audio, 0.3, "01:21:00")
  78. }

Playground链接

英文:

You can do this with a good old type switch, but it is a little bit verbose:

  1. package main
  2. import (
  3. "fmt"
  4. "strconv"
  5. )
  6. type Cut interface {
  7. int | int8 | int16 | int32 | int64 | float32 | float64 | string
  8. }
  9. type AudioObj struct {
  10. fileName string
  11. }
  12. func (A AudioObj) CutIfFirstIsString(from string, to float64) {
  13. // Cut audio file: ("00:00:03", 81.0)
  14. fmt.Println(`Cut audio file: ("00:00:03", 81.0)`)
  15. }
  16. func (A AudioObj) CutIfSecondIsString(from float64, to string) {
  17. // Cut audio file: (0.3, "01:21:00")
  18. fmt.Println(`Cut audio file: (0.3, "01:21:00")`)
  19. }
  20. func CutAudio[S Cut, E Cut](MusicFile AudioObj, start S, end E) {
  21. // The first parameter is a string.
  22. if _, ok := any(start).(string); ok {
  23. MusicFile.CutIfFirstIsString(cutToString(start), cutToFloat(end))
  24. }
  25. // The second parameter is a string.
  26. if _, ok := any(end).(string); ok {
  27. MusicFile.CutIfSecondIsString(cutToFloat(start), cutToString(end))
  28. }
  29. }
  30. func cutToString[S Cut](cut S) string {
  31. var str string
  32. switch start := any(cut).(type) {
  33. case int:
  34. str = strconv.FormatInt(int64(start), 10)
  35. case int8:
  36. str = strconv.FormatInt(int64(start), 10)
  37. case int16:
  38. str = strconv.FormatInt(int64(start), 10)
  39. case int32:
  40. str = strconv.FormatInt(int64(start), 10)
  41. case int64:
  42. str = strconv.FormatInt(start, 10)
  43. case float32:
  44. str = strconv.FormatFloat(float64(start), 'G', -1, 32)
  45. case float64:
  46. str = strconv.FormatFloat(start, 'G', -1, 64)
  47. case string:
  48. str = start
  49. }
  50. return str
  51. }
  52. func cutToFloat[S Cut](cut S) float64 {
  53. var flt float64
  54. switch end := any(cut).(type) {
  55. case int:
  56. flt = float64(end)
  57. case int8:
  58. flt = float64(end)
  59. case int16:
  60. flt = float64(end)
  61. case int32:
  62. flt = float64(end)
  63. case int64:
  64. flt = float64(end)
  65. case float32:
  66. flt = float64(end)
  67. case float64:
  68. flt = end
  69. case string:
  70. flt, _ = strconv.ParseFloat(end, 64)
  71. }
  72. return flt
  73. }
  74. func main() {
  75. audio := AudioObj{fileName: "music.mp3"}
  76. CutAudio(audio, "00:00:03", 81.0)
  77. CutAudio(audio, 0.3, "01:21:00")
  78. }

Playground link

huangapple
  • 本文由 发表于 2022年4月15日 19:25:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/71883446.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定