2009-05-31 55 views
9

Có cách nào để thực hiện điều gì đó như thế này không?Trả về kiểu dữ liệu khác nhau tùy thuộc vào dữ liệu (C++)

(correct pointer datatype) returnPointer(void* ptr, int depth) 
{ 

    if(depth == 8) 
     return (uint8*)ptr; 
    else if (depth == 16) 
     return (uint16*)ptr; 
    else 
     return (uint32*)ptr; 
} 

Cảm ơn

+0

Làm thế nào để bạn hy vọng sẽ sử dụng điều này? Rất có thể thiết kế của bạn không tuân theo các phương pháp OOP tốt nhất. – outis

+0

Adobe Photoshop SDK là những gì tôi đang sử dụng, và họ sử dụng void * – Joel

+2

Bạn có thể đưa ra một ví dụ sử dụng, bao gồm cả một cuộc gọi đến một chức năng SDK của Photoshop? – outis

Trả lời

9

số Kiểu trả về của một hàm C++ chỉ có thể khác nhau dựa trên các thông số mẫu rõ ràng hoặc loại của đối số của nó. Nó không thể thay đổi dựa trên giá trị của các đối số của nó.

Tuy nhiên, bạn có thể sử dụng các kỹ thuật khác nhau để tạo loại kết hợp một số loại khác. Thật không may điều này sẽ không nhất thiết giúp bạn ở đây, như là một kỹ thuật như vậy là void * chính nó, và nhận được trở lại loại ban đầu sẽ là một nỗi đau.

Tuy nhiên, bằng cách chuyển vấn đề bên trong ra ngoài, bạn có thể nhận được những gì bạn muốn. Tôi tưởng tượng bạn muốn sử dụng mã bạn được đăng như một cái gì đó như thế nào, ví dụ:

void bitmap_operation(void *data, int depth, int width, int height) { 
    some_magical_type p_pixels = returnPointer(data, depth); 
    for (int x = 0; x < width; x++) 
    for (int y = 0; y < width; y++) 
     p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]); 
} 

Bởi vì C++ cần phải biết loại p_pixels tại thời gian biên dịch, điều này sẽ không làm việc như nó vốn có. Nhưng những gì chúng ta có thể làm là làm cho bitmap_operation bản thân là một mẫu, sau đó quấn nó với một công tắc dựa trên độ sâu:

template<typename PixelType> 
void bitmap_operation_impl(void *data, int width, int height) { 
    PixelType *p_pixels = (PixelType *)data; 
    for (int x = 0; x < width; x++) 
    for (int y = 0; y < width; y++) 
     p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]); 
} 

void bitmap_operation(void *data, int depth, int width, int height) { 
    if (depth == 8) 
    bitmap_operation_impl<uint8_t>(data, width, height); 
    else if (depth == 16) 
    bitmap_operation_impl<uint16_t>(data, width, height); 
    else if (depth == 32) 
    bitmap_operation_impl<uint32_t>(data, width, height); 
    else assert(!"Impossible depth!"); 
} 

Bây giờ trình biên dịch sẽ tự động tạo ra ba triển khai cho bitmap_operation_impl cho bạn.

+1

Trong C#, bạn có thể trả về một kiểu đối tượng và đưa nó vào đối tượng mong muốn của bạn (miễn là bạn biết loại). Hoặc bạn có thể sử dụng Generics. Bạn có thể làm một cái gì đó tương tự trong c + +? –

+3

C++ gần nhất có kiểu đối tượng là void *. Ngoài ra còn có loại biến thể của boost. – bdonlan

+0

Tôi đoán tôi cũng sẽ sử dụng biến thể tăng cường. –

7

Nếu bạn có thể sử dụng đối số mẫu thay vì tham số bình thường, bạn có thể tạo hàm mẫu trả về loại chính xác cho mỗi giá trị depth. Trước tiên, cần phải có một số định nghĩa đúng loại theo depth. Bạn có thể xác định một mẫu với chuyên ngành cho các kích thước hơi khác nhau:

// template declaration 
template<int depth> 
struct uint_tmpl; 

// specializations for certain types 
template<> struct uint_tmpl<8> { typedef uint8_t type; }; 
template<> struct uint_tmpl<16> { typedef uint16_t type; }; 
template<> struct uint_tmpl<32> { typedef uint32_t type; }; 

Định nghĩa này có thể được sử dụng để khai báo một chức năng templated trả về đúng loại cho mỗi giá trị bit:

// generic declaration 
template<int depth> 
typename uint_tmpl<depth>::type* returnPointer(void* ptr); 

// specializations for different depths 
template<> uint8_t* returnPointer<8>(void* ptr) { return (uint8_t*)ptr; } 
template<> uint16_t* returnPointer<16>(void* ptr) { return (uint16_t*)ptr; } 
template<> uint32_t* returnPointer<32>(void* ptr) { return (uint32_t*)ptr; } 
+2

Điều này cung cấp không có lợi thế hơn một giải pháp mà không có mẫu như returnPointer8(), returnPointer16() và returnPointer32(), bởi vì các đối số mẫu phải được liên tục (được biết đến bởi các lập trình viên) anyway. – Jem

+1

Các đối số mẫu phải được biết tại thời gian biên dịch là thuộc tính chung của các mẫu. Tuy nhiên điều này linh hoạt hơn các hàm được đặt tên khác nhau, ví dụ bạn có thể xác định một cái gì đó như mẫu ptradd (void * ptr, int val) {* returnPointer (ptr) + = val; }. Điều này phù hợp với mọi chiều sâu. Với các hàm trả về có tên khác nhau, bạn cần phải khai báo một số biến thể riêng biệt của ptradd. Đối với phiên bản templated, một hàm ptradd là đủ. – sth

1

Bạn có thể cấp phát một số bộ nhớ trên heap, và trả về một khoảng trống * mà bạn đưa vào kiểu đã được cấp phát. Đó là một cách làm việc nguy hiểm và không an toàn và là một thủ thuật C cũ.

Bạn có thể trả về một liên minh chứa tất cả các kiểu dữ liệu hợp lệ (và chỉ báo chọn).

Bạn có thể sử dụng mẫu, đây là cách C++ được đề xuất cho loại điều này.

Bạn có thể cung cấp một tập hợp các hàm quá tải lấy tham số (của mỗi loại) làm tham chiếu - trình biên dịch sẽ quyết định hàm nào sẽ gọi dựa trên kiểu dữ liệu. Tôi thường thích cách này vì tôi nghĩ nó đơn giản nhất.

0

Không; bạn không thể làm điều đó trong C++. Câu trả lời đúng là trả lại void *.

Hãy suy nghĩ về nó từ phía đối diện của cuộc gọi - và từ quan điểm biên dịch của xem tại rằng:

Làm thế nào sẽ trình biên dịch có thể kiểm tra xem giá trị trả về được sử dụng một cách chính xác (như gán cho một biến của loại thích hợp, ví dụ), nếu nó không thể biết được ba loại trả về nào sẽ được trả về?

Tại thời điểm đó, khái niệm gán "một trong nhiều loại" cho giá trị trả về sẽ trở nên vô nghĩa. Kiểu trả về của một hàm không có mục đích nào khác trong cuộc sống hơn là làm cho trình biên dịch có thể thực hiện công việc của nó; trình biên dịch cần loại "một" để có thể thực hiện kiểm tra kiểu. Vì bạn không biết cái nào cho đến khi chạy, trình biên dịch không thể thực hiện kiểm tra kiểu cho bạn. Bạn phải yêu cầu trình biên dịch "ngừng cố gắng" để khớp với giá trị trả lại cho bất kỳ loại con trỏ cụ thể nào - do đó, trả lại một void *.

Nếu đối số chiều sâu của bạn được biết tại thời gian biên dịch, bạn có thể sử dụng một tập hợp mẫu như @sth được minh họa hoặc sử dụng một bộ chức năng độc lập riêng biệt hoặc sử dụng một tập hợp các chức năng liên quan. đưa trở lại đúng loại. Cái nào bạn chọn là chủ yếu là một quyết định thẩm mỹ.

Nếu giá trị depth không được biết cho đến khi thời gian chạy, thì có thể bạn nên trả lại void *.

Bây giờ, tôi giả định rằng việc triển khai thực tế của bạn thực sự làm điều gì đó để tạo con trỏ khác với mã mẫu của bạn. Mã mẫu của bạn không phải là một hàm thực tế; nó giống như cố gắng để lặp lại những gì một cast nào. A cast không phải là cuộc gọi hàm; đó là một chỉ thị của trình biên dịch để cố gắng "làm cho" nó là toán hạng thành một loại cụ thể (chính xác là 'cách', là một câu chuyện dài cho một bài đăng khác). Nó không phải là một hoạt động ngôn ngữ C++, mà là một hoạt động của trình biên dịch. Bạn không thể viết lại điều đó trong chính C++.

0

bạn có thể làm điều này:

if (sâu == 8) (uint8 *) returnPointer (void * ptr, int chiều sâu) {// vv

Các vấn đề liên quan