เรียกใช้ฟังก์ชันจากที่อยู่ผ่านฟังก์ชันเทมเพลต

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

ฟังก์ชันเทมเพลตต้องรองรับประเภทการส่งคืนทั้งแบบเป็นโมฆะและไม่เป็นโมฆะ เนื่องจากเรากำลังจัดการกับพอยน์เตอร์ของฟังก์ชัน คอมไพลเลอร์จึงไม่ควรบ่น แม้จะส่งคืน ptr ก็ตาม

ฉันคิดที่จะทำสิ่งนี้:

template<typename ReturnType, typename Address, typename... Args>
ReturnType function_caller(Address address, Args... args)
{
    ReturnType(*ptr)(Args...) = address;
    return ptr(args...);
}
int main()
{
    auto address = 0x100;
    auto address2 = 0x200;
    function_caller<void>(&address, 1, 1); // Function with return type void.
    int result = function_caller<int>(&address2, 1, 2, 3.f, "hello"); 
    // result should contain the int value we received by calling the function at 0x200

}

น่าเสียดายที่คอมไพเลอร์แสดงข้อผิดพลาด C2440: ไม่สามารถแปลงที่อยู่ address เป็น 'ReturnType (__cdecl *)(int,int)'

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

ขอบคุณและขอให้เป็นวันที่ดี!


person Troganda    schedule 25.08.2020    source แหล่งที่มา
comment
ใช่ ฉันรู้ ที่อยู่นั้นไม่ใช่ที่อยู่จริง แต่อยู่ตรงนั้นเพื่อแสดงแนวคิดเท่านั้น นี่ไม่ใช่รหัสที่ฉันจะใช้บนรันไทม์ โปรดถือว่าที่อยู่ถูกต้อง นอกจากนั้น คอมไพเลอร์ก็ไม่รังเกียจเนื่องจากมันเป็นข้อผิดพลาดรันไทม์ ไม่ใช่เวลาคอมไพล์   -  person Troganda    schedule 25.08.2020
comment
การลดพารามิเตอร์โดยอัตโนมัติก็เป็นอันตรายเช่นกัน พารามิเตอร์ที่สี่นั้น const char* หรือเป็น const char[6] ฉันค่อนข้างแน่ใจว่ามันเป็นอย่างหลัง   -  person Mooing Duck    schedule 25.08.2020
comment
หากคุณเต็มใจที่จะมีไวยากรณ์แปลก ๆ สำหรับการโทรเป็นโมฆะ คุณสามารถเกลี้ยกล่อมให้อนุมานประเภทการส่งคืนได้ coliru.stacked-crooked.com/a/42c1c60bf0e2f50a   -  person Mooing Duck    schedule 25.08.2020


คำตอบ (1)


คำตอบคือใช่ แต่การใช้เทมเพลต variadic เป็นสิ่งที่อันตราย

เพื่อบังคับให้คอมไพเลอร์ส่งที่อยู่ไปยังตัวชี้ฟังก์ชันคุณต้องใช้ reinterpret_cast หรือ c cast

หมายเหตุ: คุณกำลังส่งที่อยู่อินทิกรัลไปยังตัวชี้อย่างไม่ถูกต้อง เนื่องจากคุณพยายามส่งที่อยู่ของตัวแปรที่มีที่อยู่ไปยังตัวชี้ ไม่ใช่ที่อยู่นั้นจริงๆ !

ดังนั้นบรรทัดนี้:

function_caller<void>(&address, 1, 1); // Function with return type void.

ควรจะเป็น :

function_caller<void>(address, 1, 1); // Function with return type void.

และใช้ประเภทที่อยู่เป็น uintptr_t เสมอ ซึ่งจะเหมาะกับที่อยู่ใด ๆ ที่มีสำหรับสถาปัตยกรรม (64 บิตหรือ 32)

แต่การทำเช่นนั้นด้วยเทมเพลต variadic นั้นไม่ปลอดภัยเลย เหตุผลก็คือฟังก์ชันมีประเภทอาร์กิวเมนต์เฉพาะดังนี้:

int fn(std::string& str, const char* ptr, uint64_t& i);

แต่เมื่อคุณส่งด้วยเทมเพลต variadic คอมไพเลอร์จะอนุมานประเภทจากอาร์กิวเมนต์ที่ส่งผ่านอย่างไรก็ตามอาจจำเป็นต้องมีการแปลงบางอย่าง!

ดังนั้นในเวอร์ชันปัจจุบันของคุณ:

int i;
function_caller<int>(0x15216516, "str", "ptr", i);

การคอมไพล์จะถือว่าลายเซ็นของฟังก์ชันมีลักษณะดังนี้:

int fn(const char*, const char*, int); // wrong types means stack corruptions and undefined behaviors

ดูสิ่งนี้ด้วย:

std::string to_string(std::string_view v);

function_caller<std::string>(0x15216516, "str"); // wrong the compiler won't convert the string literal for you and the function will end up with a dangling view

function_caller<std::string>(0x15216516, std::string("str")); // wrong again there is no conversion from std::string to std::string_view here

ดังนั้นจึงเชื่อถือได้จริงๆ เพียงระบุประเภทฟังก์ชันทั้งหมด และใช้สิ่งนั้นเพื่อส่งที่อยู่เหมือนกับที่ boost.dll ทำ

person dev65    schedule 25.08.2020
comment
ไม่ พารามิเตอร์ทั้งสองจะเป็น const char[4] ไม่ใช่ const char* - person Mooing Duck; 25.08.2020