模拟接口,覆盖方法,多态并在C

时间:2018-01-30 00:41:38

标签: c interface polymorphism override superclass

一个静态库,可以通过void *接收任何结构,并且能够访问它的成员变量,无论是嵌入式结构还是指向函数的指针。

它保证作为嵌入式结构的成员变量将在那里并且可以通过super访问,并且一个成员变量是一个名为execute的函数指针。

问题 - (简而言之,如何仅使用void *访问基础结构成员)

我猜它与通用编程有关,我们是否可以通过内存地址或任何其他替代方式访问以下内容。 在编译时无法转换为特定结构 struct肯定会有一个执行指针函数和超级变量。保证。

// from src/library.c (someStruct a void *, casting to specific struct is not available)
someStruct->execute();
someStruct->super->execute();

尝试1 Reference

struct Super *pointer = someStruct + 8; // works
pointer->execute(); // prints overridden method!

// how to access second member which is a pointer to function

对任何形式的解决方案开放,无论它是低级别还是内存地址,以访问每个成员或任何其他替代方式。

Project Download

预期输出:

  

重写方法!

     

超级方法

的src / library.h

#ifndef POLY_LIBRARY_H
#define POLY_LIBRARY_H

struct Library {
    void (*passSomeStruct)(void *someStruct);
};

struct Library *newLibrary();

#endif

的src / LIBRARY.C

#include "library.h"
#include <stdlib.h>

void passSomeStruct(void *someStruct) {

    // can we somewhow access the following via memory addresses or any other alternate way.
    // casting to specific structure is not available at compile time
    // struct will sure have an execute pointer function and super variable

      someStruct->execute();
      someStruct->super->execute();
}

struct Library *newLibrary() {
    struct Library *library = malloc(sizeof(struct Library));
    library->passSomeStruct = passSomeStruct;
    return library;
}

的src / super.h

#ifndef POLY_SUPER_H
#define POLY_SUPER_H

struct Super {
    void (*execute)();
};

struct Super *newSuper();

#endif //POLY_SUPER_H

的src / super.c

#include "super.h"
#include <stdio.h>
#include <stdlib.h>

static void execute() {
    printf("super method\n");
}

struct Super *newSuper() {
    struct Super *super = malloc(sizeof(struct Super));
    super->execute = execute;
    return super;
}

测试

测试/ library_test.h

#ifndef POLY_LIBRARY_TEST_H
#define POLY_LIBRARY_TEST_H

#endif //POLY_LIBRARY_TEST_H

测试/ library_test.c

#include "../src/library.h"
#include "temp.h"

int main() {
    struct Library *library = newLibrary();
    struct Temp *temp = newTemp();
    library->passSomeStruct(temp);
    return 0;
}

测试/ temp.h

#ifndef TEST_SUPER_H
#define TEST_SUPER_H

#include <stdlib.h>

struct Temp {
    struct Super *super;
    void (*execute)();
};

struct Temp *newTemp();

#endif //TEST_SUPER_H

测试/ temp.c

#include "temp.h"
#include <stdio.h>
#include "../src/super.h"

static void execute() {
    printf("overridden method!\n");
}

struct Temp *newTemp() {
    struct Temp *temp = malloc(sizeof(struct Temp));
    temp->super = newSuper();
    temp->execute = execute;
    return temp;
}

生成文件

VPATH := src test

all: libTest

libTest: super.o library.o
    mkdir -p bin
    ar rcs bin/libTest $^

test: library_test
    ./library_test

library_test: library_test.o library.o super.o temp.o
    $(CC) -o $@ $^

%.o: %.c %.h

2 个答案:

答案 0 :(得分:1)

我担心我不能给你一个如何完成OOP的完美例子 C,无论如何都不会在几分钟之内成为我的头顶。还有其他 Stack Overflow中的这些内容更详细,就像这样 之一:

Can you write object-oriented code in C?

我强烈建议您阅读该主题,那里有很多好的答案。

我还建议,如果您真的想在代码库中使用OOP,请不要 自己写,它非常复杂,非常耗时。在那里面 我会建议您使用GObject作为代码的一部分 glib库。{我告诉你这一切,因为你说的评论

  

是的,这是我需要了解更多的部分,我不太了解该域,字节布局,转换为char*等。

当它出现这样的事情时,了解这些事情是至关重要的。

我将在评论中给出一个非常简短的例子 内存布局等

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#define TEND 0
#define TINT 1
#define TSTRING 2
#define TDOUBLE 3

typedef struct typeinfo {
    int type;
    size_t offset;
} TypeInfo;




typedef struct K1 {
    int a;
    int b;
    char c[32];
} K1;

void print_K1(K1 *obj)
{
    printf("K1: %d %s %d\n", obj->a, obj->c, obj->b);
}

TypeInfo *init_k1(K1 *obj, int a, int b, const char *c)
{
    TypeInfo *types = calloc(4, sizeof *types);

    obj->a = a;
    obj->b = b;
    strcpy(obj->c, c);

    // filling type info
    // we know the structure, we can provide this information
    types[0].type = TINT;
    types[0].offset = offsetof(K1, a);

    types[1].type = TINT;
    types[1].offset = offsetof(K1, b);

    types[2].type = TSTRING;
    types[2].offset = offsetof(K1, c);

    types[3].type = TEND;

    return types;
}

typedef struct K2 {
    int k;
    double o;
} K2;

void print_K2(K2 *obj)
{
    printf("K2: %d %lf\n", obj->k, obj->o);
}

TypeInfo *init_k2(K2 *obj, int k, double o)
{
    TypeInfo *types = calloc(3, sizeof *types);

    obj->k = k;
    obj->o = o;

    // filling type info
    // we know the structure, we can provide this information
    types[0].type = TINT;
    types[0].offset = offsetof(K2, k);

    types[1].type = TDOUBLE;
    types[1].offset = offsetof(K2, o);

    types[2].type = TEND;

    return types;
}



void somefunc(void *ptr, TypeInfo *types)
{
    char *base = ptr, *offset;

    while(1)
    {
        offset = base + types->offset;
        switch(types->type)
        {
            case TINT:
            {
                int *p = (int*) offset;
                *p *= -1;
                break;
            }

            case TSTRING:
            {
                char *p = offset;
                p[0] = ' ';
                break;
            }

            case TDOUBLE:
            {
                double *p = (double*) offset;
                *p *= 0.35;
            }

            case TEND:
                return;
        }

        types++;
    }
}

int main(void)
{
    K1 k1;
    K2 k2;

    TypeInfo *t1, *t2;

    t1 = init_k1(&k1, 33, -23, "Hello Peter");
    print_K1(&k1);
    somefunc(&k1, t1);
    print_K1(&k1);

    t2 = init_k2(&k2, 112, 122.12);
    print_K2(&k2);
    somefunc(&k2, t2);
    print_K2(&k2);

    free(t1);
    free(t2);

    return 0;
}

我有两个不同成员的结构K1K2,这意味着他们的记忆 布局不同。对于K1K2,有使用的{init}函数 helper struct TypeInfo存储结构成员的偏移量和类型 他们自己的班级。这些信息已知,因此他们可以构建 这样的信息表。 offsetof返回字段成员的偏移量 从结构类型的开始。

函数somefunc对结构没有任何了解,所以它不能 将void指针强制转换为结构的指针。然而它得到了一个列表 来自呼叫者的类型。此函数会更改每个int成员的符号, 用每个字符串替换带有空字符的第一个字母 将每两倍乘以0.35。感谢types,它可以获得成员 使用types提供的偏移量。

main函数初始化每个结构的一个对象,打印原始对象 state,用对象和类型信息调用somefunc并打印出来 对象再次。在输出中,您可以看到值已更改 相应

<强>输出

K1: 33 Hello Peter -23
K1: -33  ello Peter 23
K2: 112 122.120000
K2: -112 42.742000

虽然这可行,但我认为这不是一个优雅的解决方案。这是一个 基本的例子,但在处理更复杂的事情时会变得非常复杂 结构。这将向您展示如何访问您所在的结构的内容 只有一个void指针。但没有类型信息,这不可能 完成。出于这个原因,我鼓励你使用一些已经存在的库 实施了打字系统。 GObject非常成熟,广泛用于GTK +应用程序。 Glib和GTK +人们多年来一直在使用它,所以我会相信他们的 实施任何自我实施的解决方案。

答案 1 :(得分:0)

最后我把它揭秘了,但是我可以接受建议,任何人都可以改进这个答案,让它变得更好或者向我解释这里发生了什么?

它是某种神奇的数字8,可以解决问题。

void passSomeStruct(void *someStruct) {
    struct Super *pointer = someStruct + 8; 
    pointer->execute(); // outputs "overridden method!"

    pointer = someStruct + 16; 
    pointer->execute(); // outputs "super method"
}

修改

我的结构定义,我想知道为什么首先定义的super是16偏移量,我定义的方法execute是8偏移量,分配的内存是反向的而不是我定义了它们?

struct Temp {
    struct Super *super;
    void (*execute)();
};