erlang本身提供一个接口,可以用来检查模块是否有导出函数,这个接口是erlang:function_exported/3,但是很多时候这个接口无法正常使用。
下面重现一下这个问题:
  1. 1> erlang:function_exported(crypto,start,0).
  2. false
  3. 2> crypto:start().
  4. ok
  5. 3> erlang:function_exported(crypto,start,0).
  6. true
注意:例子中并不是说一定要crypto:start()才能使用这个函数,只是说这个函数确实存在。
 
现在,来看下erlang对这个接口的说明:
Returns true if the module Module is loaded and contains an exported function Function/Arity; otherwise false.
Returns false for any BIF (functions implemented in C rather than in Erlang).
换句话说,如果一个模块还没有被加载,就无法使用erlang:function_exported/3函数。很多模块在erlang启动时都没有加载到系统,都是在使用到的时候才加载,所以这个检查导出函数的接口可能会出现错误的结果。
 
如果要判断模块是否有导出函数,那么,我们可以像下面这么写,就可以正常使用了。
  1. -module(test).
  2. -compile(export_all).
  3. function_exported(Module, Function, Arity) ->
  4. case erlang:module_loaded(Module) of
  5. true ->
  6. next;
  7. _ ->
  8. code:ensure_loaded(Module)
  9. end,
  10. erlang:function_exported(Module, Function, Arity).

另外,如果模块被改成bif也无法判断了。不过这倒不用太过担心,我们自己写的函数都不会是bif

最后,讨论下erlang:function_exported/3为何只能检查已加载过的模块?
为了实现热更新,新旧代码替换,erlang维护了一个全局哈希表,用于描述模块导出函数信息。那么,只要加载过的模块就会被记录信息到这个哈希表,保存这些信息也要耗费一定的内存开销,而且,哈希算法本身存在冲突的可能性,元素越多,发生冲突的可能性越大,为了解决冲突,还会引入了bucket(哈希桶)或者链表。这样,如果要保证键值分散,就会浪费很多空间,不然又会影响查找效率。
所以,erlang只记录了加载过的模块信息,其他等到使用到的时候再加载。
erlang:function_exported/3是一个bif函数,实现函数可以在erts\emulator\beam\bif.c找到:
  1. BIF_RETTYPE function_exported_3(BIF_ALIST_3)
  2. {
  3. if (is_not_atom(BIF_ARG_1) || is_not_atom(BIF_ARG_2) ||  is_not_small(BIF_ARG_3)) {
  4. BIF_ERROR(BIF_P, BADARG);
  5. }
  6. if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3),
  7. erts_active_code_ix()) == NULL) {
  8. BIF_RET(am_false);
  9. }
  10. BIF_RET(am_true);
  11. }

erts_find_function实现在erts\emulator\beam\export.c中:

  1. /*
  2. * Find the export entry for a loaded function.
  3. * Returns a NULL pointer if the given function is not loaded, or
  4. * a pointer to the export entry.
  5. *
  6. * Note: This function never returns export entries for BIFs
  7. * or functions which are not yet loaded.  This makes it suitable
  8. * for use by the erlang:function_exported/3 BIF or whenever you
  9. * cannot depend on the error_handler.
  10. */
  11. Export* erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
  12. {
  13. struct export_templ templ;
  14. struct export_entry* ee;
  15. ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
  16. if (ee == NULL ||   (ee->ep->addressv[code_ix] == ee->ep->code+3 &&
  17. ee->ep->code[3] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) {
  18. return NULL;
  19. }
  20. return ee->ep;
  21. }

这里,export_tables是一个全局变量,还是在export.c,其中,code_ix被用来控制代码版本,有时间的话再讨论erlang热更新机制

  1. static IndexTable export_tables[ERTS_NUM_CODE_IX];  /* Active not locked */

所以,erlang:function_exported/3只是去查找导出函数的哈希表,找到返回true,没有就false

 
参考:http://blog.csdn.net/mycwq/article/details/40663737

erlang判断模块导出函数问题的更多相关文章

  1. DLL模块例1:使用.def模块导出函数,规范修饰名称,显示连接调用dll中函数

    以下内容,我看了多篇文章,整合在一起,写的一个例子,关于dll工程的创建,请参考博客里另一篇文章:http://www.cnblogs.com/pingge/articles/3153571.html ...

  2. 关于DLL模块导出函数

    当然以前我知道有一个.def文件的,里面写的都是需要导出的函数,以为与__declspec(dllexport)作用是一样的.但是今天看公司项目源码的时候才知道,它们两个导出方法是有一定的区别的,编译 ...

  3. driver: linux2.6 内核模块导出函数实例(EXPORT_SYMBOL) 【转】

    转自:http://blog.chinaunix.net/uid-23381466-id-3837650.html 内核版本:2.6.38-11-generic 内核自己都大量利用内核符号表导出函数, ...

  4. python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性

    python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性 inspect import inspect def fun(): pass inspect.ism ...

  5. DLL模块例2:使用__declspec(dllexport)导出函数,extern "C"规范修饰名称,隐式连接调用dll中函数

    以下内容,我看了多篇文章,整合在一起,写的一个例子,关于dll工程的创建,请参考博客里另一篇文章:http://www.cnblogs.com/pingge/articles/3153571.html ...

  6. DLL的导出函数重定向机制

    曾经,调试时跟进HeapAlloc,结果发现直接进入到ntdll的RtlAllocateHeap中,感到很有趣,就使用Dependency Walker查看kernel32.dll的导出函数,结果发现 ...

  7. AFX_MANAGE_STATE(AfxGetStaticModuleState())DLL导出函数包含MFC资源

    AFX_MANAGE_STATE(AfxGetStaticModuleState()) 先看一个例子: .创建一个动态链接到MFC DLL的规则DLL,其内部包含一个对话框资源.指定该对话框ID如下: ...

  8. dll 导出函数名的那些事

    dll 导出函数名的那些事 关键字: VC++  DLL  导出函数 经常使用VC6的Dependency或者是Depends工具查看DLL导出函数的名字,会发现有DLL导出函数的名字有时大不相同,导 ...

  9. C# 遍历DLL导出函数

    C#如何去遍历一个由C++或E语言编写的本地DLL导出函数呢 不过在这里我建议对PE一无所知的人 你或许应先补补这方面的知识,我不知道为什么PE方面的 应用在C#中怎么这么少,我查阅过相关 C#的知识 ...

随机推荐

  1. Django环境搭建(二)

    web框架 本质就是socket服务端 socket服务端:是计算机科学家在TCP/IP基础上进行封装,暴露出一个接口socket,就是一个收发数据的一个接口. 对于真实的web程序来说分为两部分:服 ...

  2. 为什么出现ORM

    ORM(Object Relational Mapping)对象关系映射,是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术 . 为什么出现ORM? 面向对象的特征:我们通常使用的开发语言J ...

  3. 洛谷 P1458 顺序的分数 Ordered Fractions

    P1458 顺序的分数 Ordered Fractions 题目描述 输入一个自然数N,对于一个最简分数a/b(分子和分母互质的分数),满足1<=b<=N,0<=a/b<=1, ...

  4. Notepad++和MinGW的安装和配置

    http://blog.csdn.net/cclovepl/article/details/70568313 http://blog.csdn.net/cclovepl/article/details ...

  5. (转)30 IMP-00019: row rejected due to ORACLE error 12899

    IMP: row rejected due IMP: ORACLE error encountered ORA: value too large , maximum: )导入日志报 IMP: 由于 O ...

  6. Loadrunner--常见问题及解决办法

    1.LR 脚本为空的解决方法: 1.去掉ie设置中的第三方支持取消掉 2.在系统属性-高级-性能-数据执行保护中,添加loadrunner安装目录中的vugen.exe文件 遇到flight界面为空的 ...

  7. Java Timer TimerTask Example(java Timer的例子)

    Java java.util.Timer is a utility class that can be used to schedule a thread to be executed at cert ...

  8. php课程 9-33 php文件操作里面的注意事项有哪些

    php课程 9-33 php文件操作里面的注意事项有哪些 一.总结 一句话总结:文件操作其实很简单,就是几个文件操作函数需要记一下. 1.文件函数如何使用(如何找php文件函数的资料)? 查看参考手册 ...

  9. LA 5902 - Movie collection 树状数组(Fenwick树)

    看题传送门 题目大意:XXX喜欢看电影,他有好多好多的影碟,每个影碟都有个独立的编号.开始是从下往上影碟的顺序是n~1,他每次拿出影碟的时候,你需要输出压在该影碟上的有几个.(拿出后其他影碟顺序不变) ...

  10. 2、qq物联环境搭建

    1.使用easyopenjtag.openjtag来烧写uboot 2.体验uboot 插讲<网络设置_ping问题解决_远程登录ssh_文件互传ftp> 192.168.1.183 wi ...