2015-11-21 1 views
1

Я портирую библиотеку Java на Swift 2.0 и испытываю некоторые проблемы с дженериками.Есть ли способ определить соответствие субпротокола, например, в соответствии с общим протоколом в Swift 2 (во время выполнения или во время компиляции)?

У меня есть следующая иерархия протоколов:

public protocol Graph { 
    typealias V: Hashable 
    typealias E: Hashable 

    func getAllEdges(sourceVertex: V, targetVertex: V) -> Set<E>? 
    func getEdge(sourceVertex: V, targetVertex: V) -> E? 
    func getEdgeFactory() -> EdgeFactory<V, E>? 
    func addEdge(sourceVertex: V, targetVertex: V) -> E? 
    func addEdge(sourceVertex: V, targetVertex: V, e: E) -> Bool 
    func addVertex(v: V) -> Bool 
    func containsEdge(sourceVertex: V, targetVertex: V) -> Bool 
    func containsEdge(e: E) -> Bool 
    func containsVertex(v: V) -> Bool 
    func edgeSet() -> Set<E> 
    func edgesOf(v: V) -> Set<E> 
    func removeAllEdges<T: CollectionType where T.Generator.Element == E>(edges: T) -> Bool 
    func removeAllEdges(sourceVertex: V, targetVertex: V) -> Set<E>? 
    func removeAllVertices<T: CollectionType where T.Generator.Element == V>(vertices: T) -> Bool 
    func removeEdge(sourceVertex: V, targetVertex: V) 
    func removeEdge(e: E) -> Bool 
    func removeVertex(v: V) -> Bool 
    func vertexSet() -> Set<V> 
    func getEdgeSource(e: E) -> V 
    func getEdgeTarget(e: E) -> V 
    func getEdgeWeight(e: E) -> Double 
} 

public protocol DirectedGraph: Graph { 
    func inDegreeOf(vertex: V) -> Int 
    func incomingEdgesOf(vertex: V) -> Set<E> 
    func outDegreeOf(vertex: V) -> Int 
    func outgoingEdgesOf(vertex: V) -> Set<E> 
} 

public protocol UndirectedGraph: Graph {  
    func degreeOf(vertex: V) -> Int 
} 

А вот определение класса, который вызывает тревогу:

public class CrossComponentIterator 
    <V: Hashable, E: Hashable, D, G: Graph 
     where G.V == V, G.E == E> 
    : AbstractGraphIterator<V, E> 

А именно, метод, который должен INIT один из переменных, исходя из фактического типа Граф передан - DirectedGraph или UndirectedGraph.

Я попытался решить это, объявив несколько версий функции, которая делает это:

func createGraphSpecifics<DG: Graph where DG: DirectedGraph, DG.V == V, DG.E == E>(graph: DG) 
    -> CrossComponentIteratorSpecifics<V, E> 
{ 
    return DirectedSpecifics<V, E, DG>(graph: graph) 
} 

func createGraphSpecifics<UG: Graph where UG: UndirectedGraph, UG.V == V, UG.E == E>(graph: UG) 
    -> CrossComponentIteratorSpecifics<V, E> 
{ 
    return UndirectedSpecifics<V, E, UG>(graph: graph) 
} 

func createGraphSpecifics<GG: Graph where GG.V == V, GG.E == E>(graph: GG) 
    -> CrossComponentIteratorSpecifics<V, E> 
{ 
    fatalError("Unknown graph type instance") 
} 

Но, к сожалению, только последняя версия функции вызывается для любого экземпляра графа (даже если оно соответствует " DirectedGraph»или„UndirectedGraph“)

и я знаю, что, наверное, я могу решить эту проблему путем преобразования протоколов DirectedGraph и UndirectedGraph абстрактных классов (я имею в виду занятия с FatalError() в каждой объявленной функции, так как Swift Безразлично» t поддержка абстрактных классов de jure).

Но может быть, есть еще одно, более элегантное и быстрое решение?

В Java это тривиально - соответствие интерфейс проверяется во время выполнения:

if (g instanceof DirectedGraph<?, ?>) { 
    return new DirectedSpecifics<V, E>((DirectedGraph<V, E>) g); 
} else { 
    return new UndirectedSpecifics<V, E>(g); 
} 

Редактировать вот минимальный код для того, что я пытаюсь достичь:

protocol P { 
    // This typealias makes impossible to use 'P' 
    // (or its descendants) as a type. 
    // It can be used only as generic constraint. 
    typealias A 

    // myfunc is needed for compiler to infer 'A' 
    func myfunc(a: A) 
} 
protocol P1:P { 
    func p1specific(a: A) 
} 
protocol P2:P { 
    func p2specific(a: A) 
} 

struct S<T:P> { 
    init(t: T) { 
     // TODO: check if 't' conforms to 'P1', 'P2', both or neither 
    } 
} 

// Examples of concrete implementations of 'P1' and 'P2' 
struct S1<X>:P1{ 
    func myfunc(a: X) {} 
    func p1specific(a: X) {} 
} 
struct S2<X>:P2{ 
    func myfunc(a: X) {} 
    func p2specific(a: X) {} 
} 
+0

Вы нашли решение? да, пожалуйста, поделитесь им. спасибо – user3441734

ответ

0

Is there any way to determine sub-protocol conformance for instance conforming to generic protocol in Swift 2 (at runtime or during compilation)?

Да.

Вот мой трюк для достижения стирания стилей, что позволяет использовать время выполнения. Наблюдайте _P:

protocol _P { 
    static var _A: Any.Type { get } 
    func _myfunc(_a: Any) -> Void? 
} 

extension _P where Self: P { 

    static var _A: Any.Type { 
     return A.self 
    } 

    func _myfunc(_a: Any) -> Void? { 
     return (_a as? A).map(myfunc) 
    } 
} 

protocol P { 
    typealias A 

    func myfunc(a: A) 
} 

protocol _P1:_P { 
    func _p1specific(_a: Any) -> Void? 
} 

extension _P1 where Self: P1 { 
    func _p1specific(_a: Any) -> Void? { 
     return (_a as? A).map(p1specific) 
    } 
} 

protocol P1:_P1, P { 
    func p1specific(a: A) 
} 

protocol _P2:_P { 
    func _p2specific(_a: Any) -> Void? 
} 

extension _P2 where Self: P2 { 
    func _p2specific(_a: Any) -> Void? { 
     return (_a as? A).map(p2specific) 
    } 
} 

protocol P2:_P2, P { 
    func p2specific(a: A) 
} 

Теперь вы можете определить, соответствует ли значение P1 или P2 и заставить актеров соответственно. Кроме того, общий параметр A теперь доступен через непрозрачный Any.Type.

(x as? _P1) != nil ? true : false

+0

и что такое «общий экземпляр параметра» в вашем «решении» ??? – user3441734

+0

@ user3441734: Я обновил свой ответ. –

+0

@ user3441734: Ваш downvote необоснован. Пожалуйста, взгляните на мое редактирование, надеюсь, это прояснит любые сомнения. –

-1
import XCPlayground 
import Foundation 

protocol P {} 
protocol P1:P {} 
protocol P2:P {} 

struct S1:P1{} 
struct S2:P2{} 

struct S<T:P> { 
    var p1: P1? 
    var p2: P2? 

    init(t: T) { 
     p1 = t as? P1 
     p2 = t as? P2 
    } 
} 

let p1 = S1() 
let p2 = S2() 
let s1 = S(t: p1) 
let s2 = S(t: p2) 

dump(s1) 
dump(s2) 
/* 
▿ S<S1> 
    ▿ p1: S1 
    - Some: S1 
    - p2: nil 
▿ S<S2> 
    - p1: nil 
    ▿ p2: S2 
    - Some: S2 
*/ 

использование

g is Type    // trur or false 

и

let v2 = v1 as? Type // v2 = v2 or nil 

в быстрой обновления

protocol P { 
    typealias A 
} 
protocol P1:P {} 
protocol P2:P {} 

struct S1:P1{ 
    typealias A = Int 
} 
struct S2:P2{ 
    typealias A = Double 
} 

struct S<T:P> { 
    var p1: S1? 
    var p2: S2? 

    init(t: T) { 
     p1 = t as? S1 
     p2 = t as? S2 
    } 
} 

let p1 = S1() 
let p2 = S2() 
let s1 = S(t: p1) 
let s2 = S(t: p2) 

.....

protocol P { 
    // This typealias makes impossible to use 'P' 
    // (or its descendants) as a type. 
    // It can be used only as generic constraint. 
    typealias A 

    // myfunc is needed for compiler to infer 'A' 
    func myfunc(a: A) 
} 
protocol P1:P {} 
protocol P2:P {} 

// aka 'abstract' conforming to P1 
struct S1:P1{ 
    typealias A = AnyObject 
    func myfunc(a: A) {} 
} 
// aka 'abstract' conforming to P2 
struct S2:P2{ 
    typealias A = Int 
    func myfunc(a: A) {} 
} 
// generic struct with type conforming to P 
struct S<T:P> { 
    init(t: T) { 
     // TODO: check if 't' conforms to 'P1', 'P2', both or neither 
     if t is S1 { 
      print("t conforms to P1, because it is type S1") 
     } 
     if t is S2 { 
      print("t conforms to P2, besause it is type S2") 
     } 
    } 
} 

let s1 = S(t: S1()) // t conforms to P1, because it is type S1 
let s2 = S(t: S2()) // t conforms to P2, besause it is type S2 

// WARNING !!!!!! 
// var s = s1 
// s = s2 // error: cannot assign value of type 'S<S2>' to type 'S<S1>' 
+1

Ваш код работает, но если вы добавите «typealias A» в «протокол P» (то есть сделайте его похожим на общий интерфейс на Java), во время компиляции вы получите следующую ошибку: «error: protocol« P1 »может использоваться только как общее ограничение, потому что оно имеет собственные или связанные требования типа ». То есть вы не можете использовать «как?» потому что P1 и P2 не являются типом, а общим ограничением. –

+0

В чем проблема? дайте мне небольшой и упрощенный примерный код. понятие абстрактного класса не работает быстро. – user3441734

+0

Конечно. Я добавил минимальный пример к вопросу. –

Смежные вопросы