วิธีเชื่อมโยงและทำให้เป็นอนุกรมฟังก์ชันโดยการโอเวอร์โหลด | ตัวดำเนินการ

ฉันกำลังพยายามหาวิธีโอเวอร์โหลด operator|() โดยทั่วไปสำหรับออบเจ็กต์คลาสฐานที่กำหนดเพื่อทำให้เป็นอนุกรมหรือลูกโซ่การเรียกใช้ฟังก์ชันที่คล้ายกับวิธีการทำงานของ pipes หรือ operator<<()... ฉันต้องการโยงพวกมันผ่านตัวดำเนินการไปป์ .. ด้วยวิธีนี้ ฉันสามารถมีชุดของฟังก์ชันแบบสแตนด์อโลนและเรียกใช้ฟังก์ชันเหล่านั้นบนออบเจ็กต์ข้อมูลเดียว... กล่าวอีกนัยหนึ่ง เพื่อทำการแปลงหลายรายการในประเภทข้อมูลเดียวกัน เช่น ในระบบสตรีมมิ่ง...

พิจารณาตัวอย่างโค้ดหลอกต่อไปนี้: โค้ดนี้อาจจะไม่คอมไพล์ ฉันไม่มีคอมไพเลอร์ที่มีประโยชน์ และฉันอาจใช้ไวยากรณ์ที่ไม่ถูกต้องสำหรับพอยน์เตอร์ฟังก์ชันหรือวัตถุฟังก์ชันเป็นพารามิเตอร์ในตัวดำเนินการ... นี่เป็นเพียง เพื่อแสดงให้เห็นรูปแบบและพฤติกรรมที่ฉันติดตาม

template<typename T>
typedef T(*Func)(T); // Function Pointer for functors-lambdas-etc... 

template<typename T>
struct pipe_object {
    T operator|(T(*Func)(T) func) {
        return func(T);
    }

    T operator()(T(*Func)(T) func) {
        return this->operator|(t, func);
    }
};

จากนั้นฉันอาจต้องการใช้สิ่งเหล่านี้:

constexpr int add_one_f(int x) {
    return (x+1);
}

constexpr int add_two_f(int x) {
   return (x+2);
}


void foo() {
    pipe_object<int> p1 = {};
    pipe_object<int> p2 = {};

    int result = p1(&add_one) | p2(&add_two); 

    // or something like...

    int result = p1 | p2; // ... etc ...

    // or something like:
    p1 = add_one | add_two | p2; // ... etc ...
}

ฉันไม่รู้วิธีเผยแพร่ intput - output ในตัวดำเนินการ |()... ฉันจะต้องโหลดเกินสองเวอร์ชันเพื่อให้สามารถจดจำ |(lhs, rhs) และ |(rhs, lhs) ได้หรือไม่

ยิ่งไปกว่านั้น จะเกิดอะไรขึ้นถ้าฉันต้องการขยายสิ่งนี้เพื่อให้ functors หรือ lambdas ของฉันรับข้อโต้แย้งหลายข้อ...

ฉันค้นหาสิ่งนี้โดย Google และพบแหล่งข้อมูลเพียงไม่กี่รายการ แต่ไม่มีสิ่งใดที่เป็นรูปธรรม เรียบง่าย หรูหรา และทันสมัยอย่างน้อยด้วยฟีเจอร์ C++17...

หากคุณรู้จักแหล่งข้อมูลที่ดีในเรื่องนี้ โปรดแจ้งให้เราทราบ!


person Francis Cugler    schedule 22.07.2020    source แหล่งที่มา
comment
@IgorTandetnik ฉันรู้ว่ามันเป็นเพียงโค้ดหลอก ... ฉันไม่มีคอมไพเลอร์ที่มีประโยชน์ ... แต่ความตั้งใจคือการรับ functor เหมือน object ... หรืออาจเป็นค่าและ functor ...   -  person Francis Cugler    schedule 22.07.2020
comment
ไม่มีตัวอย่างการใช้งานของคุณใดที่สมเหตุสมผลสำหรับฉันมากนัก ค่าของ result ในตอนท้ายควรจะเป็นเท่าใด? คุณจะเพิ่มหนึ่งหรือสอง ใน อะไร p1 และ p2 ควรมีบทบาทอะไร   -  person Igor Tandetnik    schedule 22.07.2020
comment
@Ignor พิจารณาวัตถุเช่นเวกเตอร์ 2D ... สมมติว่ามันมีค่าอยู่แล้ว ... เช่น vec2 v2 = {3,5}... จากนั้นฉันก็อยากจะทำสิ่งที่ชอบ: v2 = rotate(30) | scale(5) | translate(15); จากนั้นมันจะหมุน 30 องศา หรือเรเดียน ให้ปรับขนาดเป็น 5 หน่วย แล้วแปลเป็น 15... เกือบจะเป็นวิธีการทำงานของ linux's pipes...   -  person Francis Cugler    schedule 22.07.2020
comment
คุณควบคุมคำจำกัดความของ vec2 หรือไม่ คุณช่วยมอบตัวดำเนินการมอบหมายที่จะยอมรับออบเจ็กต์ เทมเพลตนิพจน์ ที่แสดงถึงลำดับของการแปลงนี้ได้ไหม   -  person Igor Tandetnik    schedule 22.07.2020
comment
@IgorTandetnik นี่จะเป็นคลาสที่กำหนดเองของฉัน ใช่แล้ว... ฉันมีลำดับชั้นของคลาส Component ที่ใช้การออกแบบ CRTP อยู่แล้ว และนี่คือตัวดำเนินการที่ฉันต้องการเพิ่มลงในคลาสฐานเพื่อให้คลาสส่วนประกอบทั้งหมด สามารถใช้การดำเนินการนี้ในรูปแบบที่เปลี่ยนแปลงได้... ผู้ปฏิบัติงานสามารถอยู่นอกชั้นเรียนและเพียงแค่รับวัตถุหรือกำหนดภายใน... วิธีใดวิธีหนึ่งที่เหมาะกับฉัน...   -  person Francis Cugler    schedule 22.07.2020
comment
จากนั้น ก) คุณอาจต้องการยกตัวอย่างที่สร้างแรงบันดาลใจที่แท้จริงของคุณในคำถาม เพราะสิ่งที่คุณมีตอนนี้ไม่สมเหตุสมผล และ ข) อย่างที่ฉันบอกไป เทคนิคที่คุณกำลังมองหาเรียกว่าเทมเพลตนิพจน์ คุณควรหาตัวอย่างบางส่วนหากคุณค้นหาสิ่งนั้น   -  person Igor Tandetnik    schedule 22.07.2020
comment
@Igor ใช่ฉันคุ้นเคยกับ CRTP ... และฉันกำลังค้นหา operator|() overloading, Pipelining, Chaining ฯลฯ ... ฉันจะต้องดูที่ expression templates... ตอนนี้มีคุณสมบัติใหม่ภายในหรือไม่ c ++ 17 ที่สามารถใช้เพื่อทำให้สิ่งนี้ง่ายขึ้นได้อย่างไร ฉันยังไม่มีคอมไพเลอร์ C++20 เพื่อใช้ concepts, ranges ฯลฯ   -  person Francis Cugler    schedule 22.07.2020
comment
@IgorTandetnik หากพบลิงก์นี้ แสดงว่ามีตัวอย่างที่ค่อนข้างมีประโยชน์... pfultz2 .com/blog/2014/09/05/pipable-functions ที่คล้ายกับสิ่งที่ฉันตามหา... แต่ฉันคิดว่ามันเพิ่มความซับซ้อนมากเกินไป... ฉันมีคลาสพื้นฐานหนึ่งคลาส Component และจาก ซึ่งผมอาจมี abstract อยู่ 4 ประเภท และแต่ละประเภทสามารถมีได้ 3-10 รูปแบบ... พอคุณเริ่มสร้างเทมเพลตทุกอย่างเพื่อทำให้โค้ดทั่วไปเริ่มสับสนเล็กน้อย...   -  person Francis Cugler    schedule 22.07.2020
comment
@IgorTandetnik ฉันได้ให้คำตอบของตัวเองแล้วตอนนี้คอมไพเลอร์ของฉัน - IDE พร้อมใช้งานสำหรับฉัน ฉันคิดว่าจากคำตอบ คุณจะเห็นได้ว่าฉันกำลังพยายามทำอะไรอยู่... แจ้งให้เราทราบว่าคุณคิดอย่างไรโดยแสดงความคิดเห็นใต้คำตอบของฉัน   -  person Francis Cugler    schedule 23.07.2020
comment
@MooingDuck ใช่นั่นคือสิ่งที่ฉันพยายามเลียนแบบ แต่ด้วยการใช้ opeator|() เนื่องจากมันไม่ค่อยโอเวอร์โหลด ... ฉันไม่ต้องการใช้ตัวดำเนินการ << หรือ >> เนื่องจากฉันจะใช้สำหรับ input และ output ของฉัน ชั้นเรียน! และเมื่อพิจารณาว่าคำสั่ง Linux มีเทคนิคการไพพ์ ฉันจึงอยากเลียนแบบสิ่งนั้นในซอร์สโค้ด c++ ของฉัน เนื่องจาก | มักใช้ในการไพพ์หรือการเชื่อมโยงคำสั่ง   -  person Francis Cugler    schedule 23.07.2020
comment
@FrancisCugler: ฉันลบความคิดเห็นของฉันออก เพราะเมื่ออ่านเพิ่มเติม คำถามของคุณทำให้ฉันงุนงงและฉันไม่เข้าใจและฉันไม่เชื่อว่ามันจะเหมือนกันเลย int result = p1(&add_one) | p2(&add_two); | ควรดำเนินการอะไรที่นี่? ดูเหมือนไม่มีใครสามารถเข้าใจได้ว่าคุณหมายถึงอะไรกับจำนวนเต็มสองตัวนั้น   -  person Mooing Duck    schedule 23.07.2020
comment
@MooingDuck มันยากที่จะอธิบายเป็นคำพูด ... แต่ลองพิจารณาประเภทที่สร้างขึ้นแล้วเช่น vec2 v2{3,5} สมมติว่าฉันต้องการทำการแปลเป็นชุดบนเวกเตอร์นั้น ... ถ้าอย่างนั้นฉันก็จะได้แบบนี้ : `v2 | แปล(2.5) | หมุน(30) | แปล(3) | ขนาด (2); จากนั้นมันจะแปลงเวกเตอร์นั้นไป 2.5 หน่วย หมุนมันไป 30 องศาหรือเรเดียน จากนั้นแปลไป 3 หน่วย แล้วปรับขนาดเป็น 2 ตามลำดับ เป็นลำดับการดำเนินการที่ทำกับข้อมูลประเภทเดียว! เวกเตอร์เป็นเพียงการแสดง...   -  person Francis Cugler    schedule 23.07.2020
comment
@FrancisCugler นั้นง่ายมาก ขึ้นอยู่กับว่าคุณสามารถแก้ไข transform และ rotate และเช่นนั้นได้หรือไม่   -  person Mooing Duck    schedule 23.07.2020
comment
@Mooing แทนที่จะมีโค้ดเช่น v2.translate(2.5); v2.rotate(30); v2.scale(10) ฉันต้องการใช้ตัวดำเนินการ | เพื่อทำหน้าที่เหล่านั้นบนดาต้าไพพ์นั้นในการเชื่อมโยงบรรทัดเดียวหรือไพพ์คำสั่ง   -  person Francis Cugler    schedule 23.07.2020
comment
@MooingDuck ฉันไม่ได้ใช้ predefined library มันเป็นโปรเจ็กต์ของฉันเอง ดังนั้นคลาสทั้งหมดของฉันจึงเป็นของฉันเอง ... ฉันสามารถควบคุมการใช้งานและอินเทอร์เฟซได้อย่างสมบูรณ์ ... ฉันแค่ต้องการทราบวิธีทำสิ่งนี้โดยทั่วไปเพื่อที่จะได้ สามารถทำงานได้กับวัตถุใด ๆ ของฉันโดยไม่ต้องเขียนตัวดำเนินการนี้ใหม่สำหรับทุกคลาส....   -  person Francis Cugler    schedule 23.07.2020
comment
@MooingDuck ตอนนี้ด้วยสองคลาสที่ฉันแสดงด้านล่าง ฉันสามารถให้คลาสอื่น ๆ ของฉันสืบทอดจากพวกเขาในแบบ CRTP และนั่นอาจทำให้ฉันมีคลาสอ็อบเจ็กต์อื่น ๆ ที่มีคุณสมบัตินี้...   -  person Francis Cugler    schedule 23.07.2020


คำตอบ (3)


ก่อนอื่นฉันคิดว่าคุณมีพื้นฐานบางอย่างที่มีลักษณะเช่นนี้

#include <iostream>
struct vec2 {
    double x;
    double y;
};
std::ostream& operator<<(std::ostream& stream, vec2 v2) {return stream<<v2.x<<','<<v2.y;}

//real methods
vec2 translate(vec2 in, double a) {return vec2{in.x+a, in.y+a};} //dummy placeholder implementations
vec2 rotate(vec2 in, double a) {return vec2{in.x+1, in.y-1};}
vec2 scale(vec2 in, double a) {return vec2{in.x*a, in.y*a};}

ดังนั้นสิ่งที่คุณต้องการคือคลาสพร็อกซีสำหรับการดำเนินการ โดยที่อ็อบเจ็กต์พร็อกซีถูกสร้างขึ้นด้วยฟังก์ชันและพารามิเตอร์อื่นๆ (ฉันสร้างฟังก์ชันให้เป็นพารามิเตอร์เทมเพลต ซึ่งป้องกันการใช้พอยน์เตอร์ของฟังก์ชัน และช่วยให้เครื่องมือเพิ่มประสิทธิภาพสามารถอินไลน์ได้ ทำให้ค่าใช้จ่ายนี้เกือบเป็นศูนย์)

#include <type_traits>
//operation proxy class
template<class rhst, //type of the only parameter
     vec2(*f)(vec2,rhst)> //the function to call
class vec2_op1 {
    std::decay_t<rhst> rhs; //store the parameter until the call
public:
    vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
    vec2 operator()(vec2 lhs) {return f(lhs, std::forward<rhst>(rhs));}
};

//proxy methods
vec2_op1<double,translate> translate(double a) {return {a};}
vec2_op1<double,rotate> rotate(double a) {return {a};}
vec2_op1<double,scale> scale(double a) {return {a};}

จากนั้นคุณก็แค่ทำให้โซ่นั้นเชื่อมโยงได้

//lhs is a vec2, rhs is a vec2_operation to use
template<class rhst, vec2(*f)(vec2,rhst)>
vec2& operator|(vec2& lhs, vec2_op1<rhst, f>&& op) {return lhs=op(lhs);}

การใช้งานนั้นง่าย:

int main() {
    vec2 v2{3,5};
    v2 | translate(2.5) | rotate(30) | translate(3) | scale(2);
    std::cout << v2;
}

http://coliru.stacked-crooked.com/a/9b58992b36ff12d3

หมายเหตุ: ไม่มีการจัดสรร ไม่มีตัวชี้ ห้ามคัดลอกหรือย้าย สิ่งนี้ควรสร้างโค้ดเดียวกันกับที่คุณเพิ่งทำ v2.translate(2.5); v2.rotate(30); v2.scale(10); โดยตรง

person Mooing Duck    schedule 23.07.2020
comment
ไม่ใช่อย่างที่ฉันกำลังมองหา 100% อย่างไรก็ตาม เทคนิคนี้มีประโยชน์มาก! ฉันชอบที่คุณชี้ให้เห็น Zero Overhead โดยไม่จำเป็นต้องใช้ตัวชี้ฟังก์ชัน... สิ่งเดียวที่นี่คือถ้าฉันมีชุดคลาสที่แตกต่างกัน ฉันจะต้องปรับใช้สิ่งนี้กับแต่ละชุด... อย่างไรก็ตาม เมื่อฉัน มาทำความรู้จักกับแพทเทิร์นกันเถอะ ไม่ยากเลยที่จะทำซ้ำ! - person Francis Cugler; 23.07.2020
comment
ต่อไปเมื่อฉันมีคอมไพเลอร์ C ++ 20 ที่เข้ากันได้อย่างสมบูรณ์ (ยังอยู่ใน C + 17) ฉันควรจะใช้ concepts, ranges ฯลฯ ... เพื่อทำให้สิ่งนี้เป็นเรื่องเล็กน้อยยิ่งขึ้นไปอีก! - person Francis Cugler; 23.07.2020
comment
คุณสามารถใช้ SFINAE เพื่อขยายการใช้งานครั้งเดียวไปยังคลาสใดๆ ที่ตรงกับชุดข้อจำกัด มันก็ไม่ใช่เรื่องยากที่จะขยายสิ่งนี้ไปยังสิ่งต่าง ๆ ที่ค่าตอบแทนต่างกัน myclass | classToString | stringToInteger;. - person Mooing Duck; 23.07.2020
comment
@FrancisCugler: อีกตัวเลือกหนึ่งคือ std::bind ซึ่งมีเวทย์มนตร์เทมเพลตน้อยกว่าที่คุณเขียนและ มีความยืดหยุ่นมากกว่า แต่ยังละเอียดกว่าที่ไซต์การโทรด้วย - person Mooing Duck; 23.07.2020

สำหรับตัวอย่างเวกเตอร์ที่คุณเพิ่มในความคิดเห็น คุณอาจมีไวยากรณ์เช่นนี้

MyVec vec = {1, 2};
auto vec2 = vec | rotate(90) | scale(2.0) | translate(1.0,2.0);

วิธีการทำงานเป็นไปตามตรรกะต่อไปนี้:

class Transform {
public:
  virtual ~Transform () = default;
  virtual MyVector apply (const MyVector& in) const = 0;
};
inline MyVector operator| (const MyVector& v, const Transform& t) {
   return t.apply(v);
}

class Rotation : public Transform {
public:
  Rotation (int deg): m_deg(deg) {}
  MyVector apply (const MyVector& v) override {...}
private:
  int m_deg;
}:
Rotation rotate(int deg) { return Rotation(deg); }

และสิ่งที่คล้ายกันสำหรับตัวดำเนินการปรับขนาดและตัวดำเนินการแปล

โปรดสังเกตว่า | เป็นแบบเชื่อมโยงจากซ้ายไปขวา ดังนั้นจึงต้องป้อนอินพุตทางด้านซ้าย แต่คุณอาจนึกถึงการเพิ่มโอเวอร์โหลดให้กับแต่ละฟังก์ชันที่รับพารามิเตอร์การแปลงและเวกเตอร์อินพุต เพื่อให้คุณสามารถ:

auto vec2 = rotate(90,vec) | scale(2.0) | translate(1.0,2.0);

ไวยากรณ์หลังอาจใช้งานง่ายกว่าการเริ่มไพพ์ด้วยตัวดำเนินการพื้นฐานและเอกลักษณ์เล็กน้อย

person bartgol    schedule 22.07.2020

ตอนนี้ฉันมีคอมไพเลอร์ให้ฉันแล้วและอีกครั้งหลังจากทำงานในโครงการของฉันไปสักหน่อย นี่คือสิ่งที่ฉันคิดขึ้นมาได้...

มันไม่สมบูรณ์แบบเนื่องจาก function pointer สำหรับ operator() ต้องการพารามิเตอร์ 2 ตัวโดยเฉพาะดังที่เห็นในโค้ดตัวอย่างนี้...

(ฉันอาจต้องใช้เทมเพลต variadic สำหรับสิ่งนี้ เช่นเดียวกับลายเซ็นสำหรับตัวชี้ฟังก์ชันเพื่ออนุญาตให้มีตัวชี้ฟังก์ชัน วัตถุฟังก์ชัน functor หรือนิพจน์แลมบ์ดาพร้อมพารามิเตอร์จำนวนเท่าใดก็ได้ในประเภทที่แตกต่างกันไป เป็นที่ยอมรับ...)

นี่คือซอร์สโค้ดการทำงานของฉัน...

#pragma once    

#include <exception>
#include <iostream>
#include <memory>

template<typename T>
class pipe;

template<typename T>
class pipe_object {
private:
    T t_;
public:
    explicit pipe_object(T t) : t_{ t } {}

    explicit pipe_object(pipe<T> pipe) : t_{ pipe.current()->value() } {}

    pipe_object(const pipe_object<T>& other) {
        this->t_ = other.t_;
    }

    pipe_object<T>& operator=(const pipe_object<T>& other) {
        this->t_ = other.t_;
        return *this;
    }

    T value() const { return t_; }

    T operator()() {
        return t_;
    }
};

template<typename T>
class pipe {
private:
    std::shared_ptr<pipe_object<T>> current_;
    std::shared_ptr<pipe_object<T>> next_;

public:
    explicit pipe(T t) : 
        current_{ nullptr },
        next_{ nullptr } 
    {
        current_.reset(new pipe_object<T>(t));
    }

    pipe_object<T>* current() const { return current_.get(); }
    pipe_object<T>* next() const { return next_.get(); }

    T operator|(pipe<T> in) {
        pipe_object<T>* temp = current_.get();
        next_.reset(new pipe_object<T>(in));
        current_ = next_;
        return temp->value();
    }

    T operator()(T a, T b, T(*Func)(T,T)) {
        return Func(a,b);
    }
};

constexpr int add(int a, int b) {
    return a + b;
}

int main() {
    try {    
        pipe<int> p1(1);
        pipe<int> p2(3);

        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                int x = p1(i,j, &add) | p2(i,j, &add);
                std::cout << x << ' ';                
            }
            std::cout << '\n';
        }
        // Game game;      
        // game.run();      
    }
    catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

และนี่คือผลลัพธ์ที่ให้ฉัน:

0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10
2 3 4 5 6 7 8 9 10 11
3 4 5 6 7 8 9 10 11 12
4 5 6 7 8 9 10 11 12 13
5 6 7 8 9 10 11 12 13 14
6 7 8 9 10 11 12 13 14 15
7 8 9 10 11 12 13 14 15 16
8 9 10 11 12 13 14 15 16 17
9 10 11 12 13 14 15 16 17 18

ดูเหมือนว่าไปป์ operator|() จะใช้ได้กับคลาส pipe ของฉัน... และฉันต้องใช้ shared_ptr ของ pipe_object สำหรับอินสแตนซ์ปัจจุบันและอินสแตนซ์ถัดไป... pipe_object เป็นเพียง wrapper พื้นฐานสำหรับบางประเภท T คุณลักษณะพิเศษเพียงอย่างเดียวของคลาส pipe_object คือหนึ่งในตัวสร้างของมัน... ตัวสร้างที่ใช้วัตถุ pipe และแยกค่าของ pipe นั้นออกมาใช้เพื่อสร้าง pipe_object ใหม่ ฉันต้องใช้พอยน์เตอร์สองตัวเนื่องจากลักษณะของ opeator|() เป็นการเชื่อมโยงของมือขวา...

ดังที่คุณเห็นจากซอร์สโค้ดด้านบนภายใน double for loop... ฉันกำลังใช้อ็อบเจ็กต์ของ pipe operator() เพื่อส่งผ่านค่าและที่อยู่ไปยังฟังก์ชัน... ฉันยังสามารถ pipe อ็อบเจ็กต์เหล่านี้ได้โดยไม่ต้องเรียก operator() .. ขั้นตอนต่อไปคือการใช้แนวคิดนี้ แต่เพื่อให้เป็นเรื่องทั่วไปสำหรับวัตถุทุกประเภท... เว้นแต่ฉันจะใช้คลาสเหล่านี้เป็น wrapper เพื่อใช้เทคนิคการผูกมัดท่อ! ฉันหมายถึงการให้คลาสอื่นของฉันสืบทอดจากคลาส pipe นี้เหมือนกับที่ใครๆ จะทำโดยใช้ CRTP

แจ้งให้เราทราบสิ่งที่คุณคิด!

person Francis Cugler    schedule 23.07.2020
comment
แม้ว่าหลังจากอ่านโค้ดของคุณแล้ว ฉัน ยังคง ยังไม่เข้าใจว่าคุณต้องการให้ operator| ทำอะไร เหมือนจะทำให้ซ้ายเท่ากับขวาแล้วกลับมาเหมือนเดิม ฉันค่อนข้างแน่ใจว่าสิ่งนี้สามารถทำให้ง่ายขึ้นอย่างมาก - person Mooing Duck; 23.07.2020
comment
@MooingDuck ฉันต้องการให้ประเมินฟังก์ชันที่ถูกนำไปใช้จากซ้ายไปขวาและใช้ฟังก์ชันนั้นตามที่พบ ... - person Francis Cugler; 23.07.2020